1.0.0 Why is data visualization important?
How do we extract meaningful insights from our data? If you have
previously explored Anscombe’s
quartet you’ll know that, as scientists and lay people, we can
sometime be obsessed with summary statistics - mean, median, mode,
standard deviation. While these values are a helpful way to quickly
assess a population, they can be flawed to the point of deception.
Instead, we should temper our investigations by visualizing our data. A
deeper understanding of your data trends and potential models comes from
dissecting attributes of your data which can jump out more easily
through visualization.
Equally important is the ability to convey our findings to others.
The right visualizations, whether simplistic or complex, should
effectively communicate our key message.
1.1.0 The grammar of graphics
One approach to effective data visualization relies on the Grammar of
Graphics framework originally proposed by Leland Wilkinson (2005). The
idea of grammar can be summarized as follows:
- Grammar is the foundational set of rules that
define the components of a language.
- A language is built on a structure that consists of syntax
and semantics.
The grammar of graphics facilitates the concise description of any
components of any graphics. Hadley Wickham of tidyverse
fame has proposed a variant on this concept - the layered
grammar of graphics framework. By following a layered
approach of defined components, it can be easy to build a
visualization.
We can break down the above pyramid by the base components, building
from the bottom upwards.
1. Data: your visualization always starts here. What are the
dimensions you want to visualize. What aspect of your data are you
trying to convey?
2. Aesthetics: assign your axes based on the data dimensions you have
chosen. Where will the majority of the data fall on your plot? Are there
other dimensions (such as categorically encoded groupings) that can be
conveyed by aspects like size, shape, colour, fill, etc.
3. Scale: do you need to scale/transform any values to fit your data
within a range? This includes layers that map between the data and the
aesthetics.
4. Geometric objects: how will you display your data within your
visualization. Which geom_* will you use?
5. Statistics: are there additional summary statistics that should be
included in the visualization? Some examples include central tendency,
spread, confidence intervals, standard error, etc.
6. Facets: will generating subplots of the data add a dimension to
our visualization that would otherwise be lost?
7. Coordinate system: will your visualization follow a classic
cartesian, semi-log, polar, etc. coordinate system?
1.2.0 Cumulative caseload data by PHU
Let’s look at a summarized data set this week with cumulative case
counts across all PHUs in Ontario. What is nice about this data set is
that it also carries some population data with it to give us a sense of
proportions. This data comes from a period where cumulative case counts
still had some meaning to them (March 2022) so we will use it to help
demonstrate some of the information we want to visualize.
Steps we’ll take in working with this data:
- Open up the file
data/COVID-19_map_data_220303.csv with
read_csv()
- Adjust the column names
- Drop the “recent” data but keep the cumulative data
Let’s open that up with our friend read_csv()
# Read in PHU_population_information.csv
phu_information.df <- read_csv("./data/COVID-19_map_data_220303.csv")
Rows: 35 Columns: 19── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): Geographic area
dbl (18): Recent Case Count, Recent Case Rate, Cumulative Case Count, Cumulative Case Rate, Cumulative Hospitalizations Count, Cumulative Hospitalizations Rate, Cumulative Deat...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Check the structure and preview the data
str(phu_information.df)
spc_tbl_ [35 × 19] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
$ Geographic area : chr [1:35] "Ontario" "Algoma Public Health" "Brant County Health Unit" "Chatham-Kent Public Health" ...
$ Recent Case Count : num [1:35] 25751 733 233 322 876 ...
$ Recent Case Rate : num [1:35] 175 622 152 302 151 ...
$ Cumulative Case Count : num [1:35] 1107408 5275 9908 6924 48526 ...
$ Cumulative Case Rate : num [1:35] 7516 4476 6452 6494 8342 ...
$ Cumulative Hospitalizations Count : num [1:35] 42132 206 369 278 2252 ...
$ Cumulative Hospitalizations Rate : num [1:35] 286 175 240 261 387 ...
$ Cumulative Deaths Count : num [1:35] 12497 33 76 72 523 ...
$ Cumulative Deaths Rate : num [1:35] 84.8 28 49.5 67.5 89.9 62.3 98.2 28.4 70.8 49.8 ...
$ At least one dose count : num [1:35] 12489709 97881 122613 85128 474994 ...
$ Completed primary series count : num [1:35] 11947990 93941 117653 81743 454665 ...
$ At least one dose coverage (%) : num [1:35] 84.8 83.1 79.8 79.8 81.7 84.3 81.7 76.6 76.5 82.6 ...
$ Completed primary series coverage (%) : num [1:35] 81.1 79.7 76.6 76.7 78.2 81.1 78.1 73.7 73.8 79.6 ...
$ At least one dose count among eligible : num [1:35] 12486011 97873 122608 85120 474910 ...
$ Completed primary series count among eligible : num [1:35] 11946134 93935 117650 81737 454628 ...
$ At least one dose coverage (%) among eligible : num [1:35] 89.1 86.7 84.6 83.8 86 89.3 85.9 80.8 80.4 86 ...
$ Completed primary series coverage (%) among eligible: num [1:35] 85.3 83.2 81.2 80.5 82.4 85.9 82.2 77.7 77.6 82.9 ...
$ Population : num [1:35] 14734014 117840 153558 106620 581722 ...
$ Eligible population : num [1:35] 14010998 112839 144945 101565 552042 ...
- attr(*, "spec")=
.. cols(
.. `Geographic area` = col_character(),
.. `Recent Case Count` = col_double(),
.. `Recent Case Rate` = col_double(),
.. `Cumulative Case Count` = col_double(),
.. `Cumulative Case Rate` = col_double(),
.. `Cumulative Hospitalizations Count` = col_double(),
.. `Cumulative Hospitalizations Rate` = col_double(),
.. `Cumulative Deaths Count` = col_double(),
.. `Cumulative Deaths Rate` = col_double(),
.. `At least one dose count` = col_double(),
.. `Completed primary series count` = col_double(),
.. `At least one dose coverage (%)` = col_double(),
.. `Completed primary series coverage (%)` = col_double(),
.. `At least one dose count among eligible` = col_double(),
.. `Completed primary series count among eligible` = col_double(),
.. `At least one dose coverage (%) among eligible` = col_double(),
.. `Completed primary series coverage (%) among eligible` = col_double(),
.. Population = col_double(),
.. `Eligible population` = col_double()
.. )
- attr(*, "problems")=<externalptr>
head(phu_information.df)
Looking at the data itself, we want to perform the following
wrangling actions:
1. Rename the variable names to lower case.
2. In the variable names, replace all of the spaces with
_ characters.
3. Update the values in the Geographic area variable to
remove excess information.
4. Drop the variables Recent Case Count and
Recent Case Rate.
5. Drop all of the vaccination variables (containing the words
“Completed” or “dose”). We’ll use a selection helper
matches() to accomplish this.
6. Remove the prefix word “Cumulative” from any variable names.
7. Rename the Geographic area variable to
public_health_unit.
unique(phu_information.df$'Geographic area')
[1] "Ontario" "Algoma Public Health"
[3] "Brant County Health Unit" "Chatham-Kent Public Health"
[5] "City of Hamilton Public Health Services" "Durham Region Health Department"
[7] "Eastern Ontario Health Unit" "Grey Bruce Health Unit"
[9] "Haldimand-Norfolk Health Unit" "Haliburton, Kawartha, Pine Ridge District Health Unit"
[11] "Halton Region Public Health" "Hastings Prince Edward Public Health"
[13] "Huron Perth Health Unit" "Kingston, Frontenac and Lennox & Addington Public Health"
[15] "Lambton Public Health" "Leeds, Grenville & Lanark District Health Unit"
[17] "Middlesex-London Health Unit" "Niagara Region Public Health"
[19] "North Bay Parry Sound District Health Unit" "Northwestern Health Unit"
[21] "Ottawa Public Health" "Peel Public Health"
[23] "Peterborough Public Health" "Porcupine Health Unit"
[25] "Public Health Sudbury & Districts" "Region of Waterloo Public Health and Emergency Services"
[27] "Renfrew County and District Health Unit" "Simcoe Muskoka District Health Unit"
[29] "Southwestern Public Health" "Thunder Bay District Health Unit"
[31] "Timiskaming Health Unit" "Toronto Public Health"
[33] "Wellington-Dufferin-Guelph Public Health" "Windsor-Essex County Health Unit"
[35] "York Region Public Health"
# Take our PHU information and tidy it up a bit
phu_information.df %<>%
# Rename variables to lower case
rename_with(str_to_lower) %>%
# Replace variable name spaces with _
rename_with(str_replace_all, pattern=r"(\s)", replacement="_") %>%
# Rename the values in geographic_area
mutate(geographic_area = str_remove_all(.$geographic_area,
pattern=r"(^Public\sHealth\s|\sPublic.*|\sand\sDistrict\s.*|\sDistrict\s.*|\sHealth.*)"
)) %>%
# Drop the "Recent" data
select(-2, -3) %>%
# Remove any variables containing the words "Completed" or "dose"
select(-matches("Completed|dose")) %>%
# Rename the "Cumulative" variables
rename_with(str_replace_all, pattern="cumulative_", replacement = "") %>%
# Rename "geographic_area" to "public_health_unit"
rename(public_health_unit = geographic_area)
head(phu_information.df)
str(phu_information.df)
tibble [35 × 9] (S3: tbl_df/tbl/data.frame)
$ public_health_unit : chr [1:35] "Ontario" "Algoma" "Brant County" "Chatham-Kent" ...
$ case_count : num [1:35] 1107408 5275 9908 6924 48526 ...
$ case_rate : num [1:35] 7516 4476 6452 6494 8342 ...
$ hospitalizations_count: num [1:35] 42132 206 369 278 2252 ...
$ hospitalizations_rate : num [1:35] 286 175 240 261 387 ...
$ deaths_count : num [1:35] 12497 33 76 72 523 ...
$ deaths_rate : num [1:35] 84.8 28 49.5 67.5 89.9 62.3 98.2 28.4 70.8 49.8 ...
$ population : num [1:35] 14734014 117840 153558 106620 581722 ...
$ eligible_population : num [1:35] 14010998 112839 144945 101565 552042 ...
# View the updated PHU list
unique(phu_information.df$public_health_unit)
[1] "Ontario" "Algoma" "Brant County"
[4] "Chatham-Kent" "City of Hamilton" "Durham Region"
[7] "Eastern Ontario" "Grey Bruce" "Haldimand-Norfolk"
[10] "Haliburton, Kawartha, Pine Ridge" "Halton Region" "Hastings Prince Edward"
[13] "Huron Perth" "Kingston, Frontenac and Lennox & Addington" "Lambton"
[16] "Leeds, Grenville & Lanark" "Middlesex-London" "Niagara Region"
[19] "North Bay Parry Sound" "Northwestern" "Ottawa"
[22] "Peel" "Peterborough" "Porcupine"
[25] "Sudbury & Districts" "Region of Waterloo" "Renfrew County"
[28] "Simcoe Muskoka" "Southwestern" "Thunder Bay"
[31] "Timiskaming" "Toronto" "Wellington-Dufferin-Guelph"
[34] "Windsor-Essex County" "York Region"
We’re now ready to start visualizing our data, but how will we go
about doing it?
1.3.0 Which aspects of the data do we wish to highlight?
Let’s take a look at the following
chart from from Dr. Andrew V. Abela.

From the flowchart above we’ll mainly explore looking at
relationships and composition using our dataset as a basis for our
visualizations. We’ll cover distributions and comparison with a more
complex dataset later in this lecture.
2.0.0 Visualizations help us identify relationships and
correlations
Depending on the nature of your data and its dimensions there are a
number of plots to choose from to represent and convey relationships
between your variables. These various plots can reveal trends,
correlations, and ultimately relationships between variables. For
instance, as an initial form of graphical assessment, scatterplots build
a framework for exploring our data further.
| Finding correlations in your data |
Scatterplot |
|
Bubble chart |
|
Lineplot |
|
Heatmap |
| Showing connections between groups |
Arc diagram |
|
Chord diagram |
|
Connection map |
|
Network diagram |
|
Non-ribbon chord diagram |
|
Tree diagram |
| Show relationships and connections between the
data |
Heatmap |
| or showing correlations between two or more
variables |
Marimekko Chart |
|
Parallel coordinates plot |
|
Radar chart |
|
Venn diagram |
In our first lecture, we covered the creation of lineplots. For
today, we’ll focus on just two relational plots: the scatterplot and
it’s variant the bubblechart. Parallel coordinate plots will also make
an appearance later on during this lecture.
2.1.0 Scatterplots and bubblecharts are geom_point()
graphs
When we try to examine relationships, one direction we can take is to
look for trends by comparing one variable against another. From an
experimental standpoint we can graph our independent variable on the
x-axis, looking for changes in our dependent variable/measurement on the
y-axis. This is most easily accomplished when both of your variables are
on a continuous scale.
When trying to show correlations in your data between two to three
variables you can use scatterplots. Remember there are some limitations
when visualizing multi-dimensional data on a two-dimensional canvas.
Overall these plots can convey to your audience the potential
correlations between your variables or separations between groups based
on their colour or shape.
2.1.1 Use the alpha parameter to help visualize
overlapping data points
Let’s begin with a scatterplot comparing the number of cases versus
hospitalizations. We’ll use the observations from each PHU as separate
datapoints. Of note, within the geom_point() layer, we’ll
be updating the alpha parameter to change the transparency
of our points.
Using the “alpha” parameter: Note that
alpha = 1 will make completely opaque points while
alpha = 0 will make completely transparent points.
As points begin to overlap, they will create increasingly opaque areas
in your visualization.
# scatter plot of case_count vs hospitalizations_count
phu_information.df %>%
# Drop the first row of "Ontario" data
slice(2:n()) %>%
# 1. Data
ggplot() +
# 2. Aesthetics
aes(x=case_count, y = hospitalizations_count) +
# set text size
theme(text = element_text(size = 10)) +
# 4. Geoms
geom_point(alpha = 0.5)

NA
2.1.2 The bubbleplot adds an obvious dimensionality to your
data
The bubbleplot, as we’ll see, brings an additional dimension to your
visualization by varying the size of your points based on a (usually)
continuous variable. This can help to highlight underlying data trends
in a more obvious fashion for your audience.
To achieve this we will set the size parameter in our
aesthetics aes() layer. By setting the size
parameter to a variable in our data, it will alter the size or our data
points. We will augment the results of this by providing a size range
through the scale_size() layer.
# bubble plot of case_count vs hospitalizations_count
phu_information.df %>%
# Drop the first row of "Ontario" data
slice(2:n()) %>%
# 1. Data
ggplot() +
# 2. Aesthetics
aes(x=case_count, y = hospitalizations_count,
size = population) + ## Set the size aesthetic
# set text size
theme(text = element_text(size = 10)) +
# 3. Scaling
## set the range for sizing our dots
scale_size(range = c(1, 10), name = "Population (M)") +
# 4. Geoms
geom_point(alpha = 0.5)

2.2.0 Scale your axes to help see your data better
From above we can see a lot of values are bunched up together at the
lower end of our scales and likewise we have a few values that are far
out on our x/y axes. To even this out, we can transform our axes to
display values on a log10 scale. We have two places where we
can accomplish this:
- Transform your data directly ie
log(case_count). Data
will be placed on a log value scale but may have less meaning to the lay
person.
- Transform your scales/tick marks. The data is untouched and the
values still have meaning to your audience without having to do
conversions in their head.
# bubble plot of case_count vs hospitalizations_count
phu_information.df %>%
# Drop the first row of "Ontario" data
slice(2:n()) %>%
# 1. Data
ggplot() +
# 2. Aesthetics
aes(x=case_count, y = hospitalizations_count, size = population) +
# set text size
theme(text = element_text(size = 10)) +
# 3. Scaling
scale_size(range = c(1, 10), name = "Population (M)") + # set the range for sizing our dots
## Set the x and y-axis to log scales
scale_x_log10() +
scale_y_log10() +
# 4. Geoms
geom_point(alpha = 0.5)

From our bubbleplot it is clear that rising case counts are tied to
increasing population size. The relationship between our log-transformed
data appears to be linear.
How do you interpret log-transformed data?: After
log-transforming both axes it looks like the relationship between
overall cases and hospitalizations is linear. While the transformation
has definitely improved the visualization, it has made the
interpretation a little harder. Overall, in this situation, you’ll
either want to model against the untransformed data or do the math
correctly and recognize that the relationship is a power function:
\[\begin{equation*}
\begin{aligned}
\text{log} y &= B + a \text{log} x &\text{where B is the
intercept of the vertical axis and a is the slope}\\[10pt]
y &= 10^{(B + a \text{log} x)}\\[10pt]
y &= 10^{B} 10^{a \text{log} x}\\[10pt]
y &= 10^{B} x^{a} &\text{Recall that } y \text{ln} x =
x^{y}\\[10pt]
\end{aligned}
\end{equation*}\]
3.0.0 Distribution plots display frequency and spread across groups
or intervals
Many students are familar with the classic bar chart. Thus far, we’ve
already used it to some extent to look at our last lecture’s PHU case
data. When comparing groups or populations for differences in their
distribution, however, the bar chart falls quite short of the ideal.
When comparing distributions, we again are often concerned with
summary statistics - mean, median, standard deviation, confidence
intervals. The nature of our data, however, is often not continuous
across an entire y-axis interval as a bar chart may suggest but rather
our population is the result of values centred, with some variance,
around a mean value.
Over the remainder of the lecture we will compare and contrast 4
types of distribution plots for their relevance and when they may be
most useful to visualize our data. When applicable to our data
visualizations, we will also display all of our data points to better
illustrate our distribution versus the visualization. We will
examine:
- Bar charts or Barplots
- Density plots
- Box and Whisker plots
- Violin plots
3.1.0 The barplot uses the geom_bar() or
geom_col() to display data
As we have already seen, the geom_bar() layer can be
used to display our data in multiple ways. The height of the bar is
proportional to either:
1. the number of observations of each group or
2. the sum of weights applied by a weight aesthetic
In our Lecture 01 examples we actually used the
value of our observations to determine height and proportions of our
groups by setting the parameter stat="identity". This was
the application of a weighting based on the values of the y-axis
variable we chose.
A simpler way to accomplish this effect is with
geom_col(), which already uses stat_identity()
by default to calculate proportions.
| geom_bar() |
Counts observations in your
data and (by default) determines height as a proportion of total (by
default) |
Only accepts x OR y parameter in
aes() |
| geom_col() |
Uses y-axis values as the height of each bar. |
Requires both x AND y parameters in
aes() |
Both of these plots use position="stack" by default and
proportions of height match observations or sums for multiple values
sharing the same x position. Such instances can be
displayed independently using position="dodge" or
position=dodge2. This is only helpful, however, when the
number of x values (ie categories) is lower, otherwise the
graph becomes crowded.
Let’s begin with a simple barplot of SARS-Cov-2-related deaths
accumulated over the course of the pandemic. Using
phu_information.df as a data source we will convey the
distribution of total deaths per PHU using the geom_col()
layer.
Recall the first row of our data is “Ontario”, representing the total
values for each variable as a sum of the other PHUs. We’ll remove that
from our data using slice() before passing it on to
ggplot().
# barplot of death counts across PHUs
phu_information.df %>%
# Remove the top row (Ontario data)
slice(2:n()) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = public_health_unit, y = deaths_count) +
theme(text = element_text(size = 20)) + # set text size
theme(axis.text.x = element_text(angle=90, hjust = 1, vjust=0.5)) + # Adjust our x-axis text
# 4. Geoms
geom_col() ## Add our bars

3.1.1 Use reorder() to sort your data as you plot
it
In the case of phu_information.df we can do a little
better by ordering our x-axis of PHUs by total
population or death counts. Since our data
table is quite simple and only a single observation for each PHU occurs,
there are a number of ways to accomplish a sort:
- Convert
population to a factor and use
fct_reorder() to sort our factors.
- Sort the entire table using
arrange() before
plotting.
- Use the
reorder() function while generating the
plot.
Let’s sort our data by PHU population size in our next example using
the R base function reorder() within our
ggplot() call.
# barplot using the reorder function
phu_information.df %>%
# Remove the top row (Ontario data)
slice(2:n()) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
### 3.1.1 We must choose a variable to sort by in the reorder function - use population
aes(x = reorder(public_health_unit, -population),
y = deaths_count) +
theme(text = element_text(size = 20)) + # set text size
theme(axis.text.x = element_text(angle=90, hjust = 1, vjust=0.5)) + # Adjust our x-axis text
# 4. Geoms
geom_col()

3.2.0 Barplots can help guide the eye
From above, using the baplots we can quickly gauge the difference
between PHUs as they get sorted by population. There’s not much need to
colour by population as well but it does add some emphasis to Toronto as
a larger population and further enforces the idea that we have sorted by
population size.
What are we missing from this visualization that would help the
reader? It would be nice to know just how much variation there is in
population size, especially in the first ~12 PHUs. How much bigger is
Peel versus the Region of Waterloo?
3.2.1 Use the fill parameter to distinguish between
groups
One simple way to enhance our barplot is through the use of fill
colour. By setting the fill parameter to
population we can shade each bar based on the population
size of each PHU. Let’s take a look.
# barplot using the reorder function
phu_information.df %>%
# Remove the top row (Ontario data)
slice(2:n()) %>%
# We must choose a variable to sort by in the reorder function - use population
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = reorder(public_health_unit, -population), y = deaths_count, fill = population) +
theme(text = element_text(size = 20)) + # set text size
theme(axis.text.x = element_text(angle=90, hjust = 1, vjust=0.5)) + # Adjust our x-axis text
### 3.2.1 Adjust the fill legends
guides(fill = guide_colourbar(title="Population\nsize")) +
# 4. Geoms
geom_col()

3.2.3 The lollipop plot: a sweet twist on the barplot.
Now we have more clarity on populations sizes between PHUs, answering
our original question that Peel region has nearly one million more
inhabitants than the Region of Waterloo. To publish this figure we would
want to fix a few additional things like the legend presentation, the
axis names and their titles. We’ll drill into these ideas more next
lecture!
On this same topic, let’s visit one last plot that can gives us some
pieces from the two variants of barplots we’ve used. The lollipop graph
clarifies that x-axis values are more singular in nature rather than
spanning a range while still visually connecting those y-axis values to
their x-axis categories.
It looks very much like it sounds, and we’ll add an extra twist to
ours by setting the point size to the population size, giving it just a
bit more of informational dimension. To accomplish this visualization
we’ll combine a geom_point() with a
geom_segment().
Aesthetics when working with multiple geoms: As
you’ll see in the following code, we’ve made some adjustments due to
working with multiple geom_* layers. Since there can be
overlapping aesthetics between geoms, you need to be cognizant of their
effects. Rather than set these parameters in the aes()
layer, set them directly in their respective geoms.
# scatter-style using the reorder function
phu_information.df %>%
# Remove the top row (Ontario data)
slice(2:n()) %>%
# Arrange our data here to clean up the code
arrange(desc(population)) %>%
# Use the updated order to reorder the public_health_unit variable
mutate(public_health_unit = factor(public_health_unit, levels = .$public_health_unit)) %>%
ggplot(.) +
# 2. Aesthetics
aes(x = public_health_unit, y = deaths_count) +
theme(text = element_text(size = 20)) + # set text size
theme(axis.text.x = element_text(angle=90, hjust = 1, vjust=0.5)) + # Adjust our x-axis text
guides(size = guide_legend(title="Population\nsize"),
colour = "none") + # Set color legend to none to remove it
# 3. Scaling
# set the range for sizing our dots
scale_size(range = c(1, 10), name = "Population (M)") +
# 4. Geoms
### 3.2.3 Make the stick of the lollipop
geom_segment(aes(x=public_health_unit, xend=public_health_unit,
y=0, yend=deaths_count)) +
### 3.2.3 Put the candy on top
geom_point(aes(size = population), colour = "orchid")

3.3.0 Barplots to convey proportion or composition
Let’s revisit our public health unit data from last lecture. We’ll
use an updated long-format version that we created and saved in a csv
format. As you might recall this dataset contains a column of dates with
each row representing an observation of new_cases reported
by a public_health_unit on that date. We have a 4th column
total_phu_new representing total cases reported across all
PHUs on that specific date.
| Date format: YYYY-MM-DD |
factor of 34 PHUs |
numeric:double |
numeric:double |
| … |
… |
… |
… |
In our first lecture we looked at the other helpful visualization
that barplots can produce: composition and proportions. Here we can
combine the proportions of categories of data to help convey an added
dimension to our data. By stacking PHUs in our “new case” data we are
now plotting new cases per month for multiple PHUs. The area of each
stack, however, also gives us a sense of proportions for each PHU that
we’ve added.
# Open our data file and define the column types rather than let the function decide.
covid_phu.df <- read_tsv("./data/Ontario_daily_change_in_cases_by_phu_long.tsv",
col_types=("Dfdd")) # Define column types here
# Take a peek at the data
head(covid_phu.df)
tail(covid_phu.df)
3.3.1 Working with Dates and the lubridate package
Last week we breezed over the idea of working with the date format in
R. It can be a rather esoteric subject but it all comes down to how
we as a society track dates. For R, the date is stored as an
integer values representing the number of days since
1970-01-01. So yes, there must exist some “negative”
dates.
The lubridate package is here to help you simplify
working with dates and has a number of helpful functions that can be
used to extract information from your date. For us, we’ll use the
floor_date() function to help round our dates down to their
Year-Month format. This function takes just two
parameters:
date: your date or vector of dates to
convert
unit: the unit by which you want to round down -
this could be year, month, …, second (if your date is tracking that
specifically)
Note that our dates will still be in a date
object format!
# What do our dates look like?
head(covid_phu.df$date)
[1] "2020-01-23" "2020-01-23" "2020-01-23" "2020-01-23" "2020-01-23" "2020-01-23"
# What do they look like after rounding down by month?
floor_date(covid_phu.df$date, unit = "month") %>% head()
[1] "2020-01-01" "2020-01-01" "2020-01-01" "2020-01-01" "2020-01-01" "2020-01-01"
3.4.0 Convert your plots to a circular layout with
coord_polar()
Generally speaking there are a number of circular or polar coordinate
plot variants ranging in complexity from the pie chart to a racetrack
chart. The coord_polar() layer takes a few parameters:
theta: determines if angles will be mapped to the x or
y variable
start: offset from the 12 o’clock position
direction: 1 = clockwise, -1 = counter-clockwise
Here’s a summary
| Pie chart |
Sliced up proportions of a circle |
Simple proportion comparison between groups |
single bar variant |
| Nightingale/Coxcomb plot |
Intervals are equal wedges but shaded areas represent
proportions |
Intervals with additional categorical proportions |
Stacked column plot, theta=x |
| Race track plot |
Intervals are split concentrically to form rings of
equal width |
Helpful if some groups are less diverse |
Stack column plot, theta=y |
3.4.1 Pie charts work best with simple data
From our pie chart description and from your own experience, you can
recall that pie charts aren’t helpful with complex data. Once the number
of categories surpasses 6-8 groups, things can get hard to visually
digest - especially if one category dominates the others.
Let’s use phu_information.df to assess the cumulative
COVID-19 case data from across our PHUs to look at the top 4 PHUs by
caseload. We’ll convert a geom_bar() barplot to our pie
chart using the coord_polar() layer.
To accomplish this feat it helps to think about how the data is being
handled. If you think about a pie chart, you can imagine it much like a
stacked barplot where the top and bottom have been curved around to meet
each other. This is a plot all about proportions and instead of
having an x-axis defined, it is the proportions for a single category or
group.
Therefore, we set the aes() parameter x to
a value of "" so there are no categorical values for
x but you will still need to set a y value
from which to determine those proportions.
# Go to the simple phu information table
phu_information.df %>%
# Sort for only a few PHUs
filter(public_health_unit %in% c("Toronto", "Peel", "York Region", "Ottawa")) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
### 3.4.1 Set the x axis correctly
aes(x="", y=case_count,
fill = public_health_unit) + # set our fill colour instead of line colour
theme_void() + # This will remove all background theme objects
theme(text = element_text(size = 20)) + # set text size
guides(fill = guide_legend(title="Public Health Unit")) +
xlab("") + # Set the x-axis label
ylab("New cases") + # Set the y-axis label
ggtitle("Total cases by health unit") +
# 3. Scaling
scale_fill_viridis_d() + # the "d" stands for discrete colour scale
# 4. Geoms
geom_bar(width=1, stat="identity", colour = "white") + # Set up our barplot here
# 7 Coordinate system
### 3.4.1 Map angles to the Y-axis
coord_polar(theta="y")

3.4.2 Nightingale rose charts emphasize proportions
Made famous by Florence
Nightingale, these plots (which she named coxcomb
plots) were used to help emphasize the proportions of soldier deaths
due to infection during the Crimean war. Each circular increment is
equally spaced to represent increasing values along the y-axis.
Visually, this gives more area representation as we radiate outwards on
the chart. This produces a chart that
- Makes distinguishing larger proportions more obvious.
- Adds a new dimension to the pie chart, allowing us to create
intervals for comparison.
- Helps connect our start and end category, especially if they don’t
represent an ordinal or continuous scale.

Florence Nightingale’s original rose chart/coxcomb plot publication.
Sourced from the Wikimedia
commons
Caution: outer segments disproportionately represent
increases in value. Their larger area produces more visual emphasis
despite the linear scale of the y-axis.
We’ll switch back to our PHU daily case data from
covid_phu.df to generate some stackable information.
Know your y-limits! Note that due to the
disproportionate area increases with increasing y-axis values, you need
to consider the range of your data. We’ve seen the high-values
in our January 2022 data so we’ll set the x-axis scale again with
scale_x_date() to range from February 2022 to January
2023.
# Start with our PHU covid data
covid_phu.df %>%
# Filter to just look at a few PHUs
filter(public_health_unit %in% c("Toronto", "Peel", "York Region", "Ottawa")) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = floor_date(date, unit = "month"), y= new_cases,
# set our fill colour instead of line colour
fill = fct_reorder(public_health_unit, new_cases, .desc=TRUE)) +
theme_bw() + # make the theme more plain
theme(text = element_text(size = 20)) + # set text size
theme(panel.grid.major.y = element_line(color="grey60")) + # darken our major y grid
guides(fill = guide_legend(title="Public Health Unit")) +
xlab("Date") + # Set the x-axis label
ylab("New cases") + # Set the y-axis label
ggtitle("New cases per day across all Ontario Public Health Units") +
# 3. Scaling
scale_x_date(limits = c(as.Date("2022-01-15"), as.Date("2023-01-15")),
# Scale the dates correctly to get a month-Year format
date_breaks = "1 month", date_labels = "%b-%Y", expand = c(0,0)) +
scale_fill_viridis_d() + # the "d" stands for discrete colour scale
# 4. Geoms
### 3.4.2 Set up our barplot here
geom_col() +
# 7 Coordinate system
### 3.4.2 Convert the barplot to a polar coordinate
coord_polar()

3.4.3 Racetrack plots are an aesthetic variant of the bar chart
You want to make another cool chart from a boring bar chart. Stacked
or otherwise, you can convert those bar plots to a racetrack or radial
bar chart. The only difference between the coxcomb plot and radial bar
chart is that instead of the default theta="x" we set it to
y. Like flipping the coordinates for a bar chart.
Beware the racetrack transformation: This is an
aesthetic transformation where each bar gets relatively longer as you
move from inside to out. Therefore, values must be judged by radians!
Our eyes can more precisely judge differences on a Cartesian bar chart.
Thus when viewing these types of charts, don’t be misled by how they
look at a casual glance!
Let’s make a set of barchart data and compare it to the racetrack
plot. Note that negative values in our dataset are plotted separately.
It is an implementation quirk of ggplot.
# Start with our PHU covid data
covid_phu.df %>%
# Filter to just look at a few PHUs
filter(public_health_unit %in% c("Toronto", "Peel", "York Region", "Ottawa")) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = floor_date(date, unit = "month"), y= new_cases,
# set our fill colour instead of line colour
fill = fct_reorder(public_health_unit, new_cases, .desc=TRUE)) +
theme(text = element_text(size = 20)) + # set text size
guides(fill = guide_legend(title="Public Health Unit")) +
xlab("Date") + # Set the x-axis label
ylab("New cases") + # Set the y-axis label
ggtitle("New cases per day across all Ontario Public Health Units") +
# 3. Scaling
scale_x_date(limits = c(as.Date("2022-01-15"), as.Date("2023-01-15")),
date_breaks = "1 month", date_labels = "%b-%Y", expand = c(0,5)) +
scale_fill_viridis_d() + # the "d" stands for discrete colour scale
# 3. Scaling
coord_flip() + ### 3.4.3 Flip the x and y axes
# 4. Geoms
geom_col() # Set up our barplot here

# Start with our PHU covid data
covid_phu.df %>%
# Filter to just look at a few PHUs
filter(public_health_unit %in% c("Toronto", "Peel", "York Region", "Ottawa")) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = floor_date(date, unit = "month"), y= new_cases,
# set our fill colour instead of line colour
fill = fct_reorder(public_health_unit, new_cases, .desc=TRUE)) +
theme_bw() + # make the theme more plain
theme(text = element_text(size = 20)) + # set text size
theme(panel.grid.major.y = element_line(color="grey60")) + # darken our major y grid
guides(fill = guide_legend(title="Public Health Unit")) +
xlab("Date") + # Set the x-axis label
ylab("New cases") + # Set the y-axis label
ggtitle("New cases per day across all Ontario Public Health Units") +
# 3. Scaling
scale_x_date(limits = c(as.Date("2022-01-15"), as.Date("2023-01-15")),
# Scale the dates correctly to get a month-Year format
date_breaks = "1 month", date_labels = "%b-%Y", expand = c(0,5)) +
scale_fill_viridis_d() + # the "d" stands for discrete colour scale
# 4. Geoms
geom_col() + # Set up our barplot here
# 7 Coordinate system
### 3.4.3 No need to flip the coordinates, just set theta=y
coord_polar(theta="y")

3.4.4 Barplots and their variants summarize the distribution of data
between groups or intervals but NOT within single populations.
Often for our purposes as scientists we are more concerned with the
distribution of replicates or measurements as a population
within a group whilst also comparing across other groups (ie
control vs treatment). Naively, we are tempted by the convenience of
Excel to simplify our data as a bar chart with some simple standard
deviation or error bars. One word concisely describes this
behaviour:
\[\text{Inappropriate}\]
Note from our examples that although we are comparing public health
units, our datapoints represent single or total observations separated
either by group or by time. At each x-axis point we are not comparing
the distribution between categories in a meaningful way. In fact, from
our first example, we can convey a near-exact message using a
dot-plot! In essence these visualizations are communicating the
categorization of samples across multiple groups. More or less, this is
about visualizing proportions.
Section 3.0.0 Comprehension Question: After
exploring the racetrack variant above, visualizing our data ranging from
February 2022 to January 2023, what is the biggest issue you might see?
What would be one way to remedy this? Is this an appropriate
visualization of our data?
# Code your plot here!
4.0.0 Density plots compute a theoretical distribution
What is the shape of our data? When we encounter experimental
populations and begin sampling or measure for specific characteristics,
we inevitably begin to reveal whether or not that characteristic has a
predictable range, median value, etc. Density plots, once given enough
sample values, begin to predict the shape or distribution of that
sampling. We sometimes use this to represent the theoretical
distribution of our original populations.
4.0.1 Distribution plots need appropriate data
In contrast to our previous datasets, we are interested in dissecting
group characterstics after sampling multiple times. Therefore, before
continuing on our journey, we need to find some data that is better
analysed as population data. Let’s open up some more SARS-CoV-2 data
from Ontario PHUs that has been broken into age and sex demographics:
Ontario_age_sex_COVID-19_cases.csv. We’ll see what we can
glean from the data.
Before we can do any visualization we’ll want to tidy up the data in
the following steps:
- Import the data with
read_csv()
- Fix the column names to remove spaces and replace them with “_”
- Pare down on the columns we want to look at. In this case we’ll be
focusing only on three indicators: total cases, hospitalizations and
cases broken down into either male or female categories.
- Summarize the data into a proportion of cases for each age group,
based on the specific total for each indicator within each PHU.
After all of our transformations, we’ll have specific proportions of
infections, deaths, and hospitalizations within each PHU for each age
group.
# Open our dataset
covid_demographics.df <- read_csv("./data/Ontario_age_sex_COVID-19_cases.csv")
Rows: 560 Columns: 21── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): Period, From date, To date, Geographic area, Age group
dbl (16): Male Cases, Male Rate, Female Cases, Female Rate, Total Cases, Total Rate, Male Hospitalizations, Male Hospitalizations Rate, Female Hospitalizations, Female Hospital...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Take a quick look at it
str(covid_demographics.df, give.attr = FALSE)
spc_tbl_ [560 × 21] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
$ Period : chr [1:560] "recent COVID-19 cases" "recent COVID-19 cases" "recent COVID-19 cases" "recent COVID-19 cases" ...
$ From date : chr [1:560] "18-Feb-23" "18-Feb-23" "18-Feb-23" "18-Feb-23" ...
$ To date : chr [1:560] "04-Mar-23" "04-Mar-23" "04-Mar-23" "04-Mar-23" ...
$ Geographic area : chr [1:560] "Ontario" "Ontario" "Ontario" "Ontario" ...
$ Age group : chr [1:560] "0 to 4" "12 to 19" "20 to 39" "40 to 59" ...
$ Male Cases : num [1:560] 56 24 188 218 24 ...
$ Male Rate : num [1:560] 15.3 3.6 8.8 11.5 4.4 ...
$ Female Cases : num [1:560] 36 54 628 649 7 ...
$ Female Rate : num [1:560] 10.4 8.4 30.7 32.9 1.3 ...
$ Total Cases : num [1:560] 98 85 838 874 49 ...
$ Total Rate : num [1:560] 13.7 6.5 20 22.6 4.6 ...
$ Male Hospitalizations : num [1:560] 4 0 3 15 1 57 54 134 0 0 ...
$ Male Hospitalizations Rate : num [1:560] 1.1 0 0.1 0.8 0.2 4 19.8 1.8 0 0 ...
$ Female Hospitalizations : num [1:560] 5 0 7 13 0 42 54 121 0 0 ...
$ Female Hospitalizations Rate : num [1:560] 1.4 0 0.3 0.7 0 2.7 13.4 1.6 0 0 ...
$ Total Hospitalizations Count : num [1:560] 9 0 10 28 1 99 108 255 0 0 ...
$ Total Hospitalizations Rate : num [1:560] 1.3 0 0.2 0.7 0.1 3.3 16 1.7 0 0 ...
$ Unknown Sex Hospitalizations : num [1:560] 0 0 0 0 0 0 0 0 0 0 ...
$ Unknown Sex Cases : num [1:560] 6 7 22 7 18 3 7 74 0 0 ...
$ Unknown Age and Sex Hospitalizations: num [1:560] 0 0 0 0 0 0 0 0 0 0 ...
$ Unknown Age and Sex Cases : num [1:560] 0 0 0 0 0 0 0 4 0 0 ...
tail(covid_demographics.df)
# Format our dataset to focus on total_cases and total_deaths
covid_demographics_total.df <-
covid_demographics.df %>%
#---------- Fix column names ----------#
# convert column names to lowercase and replace spaces with "_". Does this look familiar?
rename_with(., ~ str_to_lower(str_replace_all(string = .x,
pattern = r"(\s)",
replacement = "_"))) %>%
# clean up PHU names
mutate(geographic_area = str_remove_all(geographic_area,
pattern=r"((^Public\sHealth\s)|(\sPublic.*)|(\sHealth.*))"),
period = str_remove_all(string = period, pattern = r"(\sCOVID.*)")
) %>%
#---------- Filter and fix variables ----------#
# Filter out some of the excess data
filter(geographic_area != "Ontario", # Drop Ontario data
age_group != "All ages") %>% # Drop combined age data
# Pare down the dataset to just total_cases, total_deaths, and total_hospitalizations
select(period, from_date, to_date, geographic_area, age_group,
total_cases, total_rate,
total_hospitalizations_count, total_hospitalizations_rate,
male_cases, female_cases) %>%
# Convert the age_group into a factor
mutate(age_group = factor(age_group),
# after converting to factor, the "5 to 11" will be in the wrong position. Relevel age_group
age_group = fct_relevel(age_group, "5 to 11", after=1)) %>%
# Rename the geographic_area variable
rename(public_health_unit = geographic_area) %>%
#---------- Summarize data ----------#
# Group the data so you can manipulate based on PHU in the next steps
group_by(period, public_health_unit) %>%
# generate percent values for each age group within a PHU
mutate(percent_cases = total_cases/sum(total_cases),
# generate percent hospitalizations for each age group within a PHU
percent_hospitalizations = total_hospitalizations_count/sum(total_hospitalizations_count),
# generate percent male and female cases for each age group within a PHU
# We'll use this data later!
percent_male_cases = male_cases/(total_cases),
percent_female_cases = female_cases/(total_cases)
)
# Take a look at the different age demographics
levels(covid_demographics_total.df$age_group)
[1] "0 to 4" "5 to 11" "12 to 19" "20 to 39" "40 to 59" "60 to 79" "80+"
str(covid_demographics_total.df, give.attr = FALSE)
gropd_df [476 × 15] (S3: grouped_df/tbl_df/tbl/data.frame)
$ period : chr [1:476] "recent" "recent" "recent" "recent" ...
$ from_date : chr [1:476] "18-Feb-23" "18-Feb-23" "18-Feb-23" "18-Feb-23" ...
$ to_date : chr [1:476] "04-Mar-23" "04-Mar-23" "04-Mar-23" "04-Mar-23" ...
$ public_health_unit : chr [1:476] "Algoma" "Algoma" "Algoma" "Algoma" ...
$ age_group : Factor w/ 7 levels "0 to 4","5 to 11",..: 1 3 4 5 2 6 7 1 3 4 ...
$ total_cases : num [1:476] 4 2 15 24 1 19 8 2 1 4 ...
$ total_rate : num [1:476] 83.6 22.2 57.6 84.4 12.5 ...
$ total_hospitalizations_count: num [1:476] 0 0 0 2 0 8 2 0 0 0 ...
$ total_hospitalizations_rate : num [1:476] 0 0 0 7 0 24.3 25.1 0 0 0 ...
$ male_cases : num [1:476] 2 0 4 9 0 11 4 1 0 0 ...
$ female_cases : num [1:476] 2 2 11 15 1 8 4 1 1 4 ...
$ percent_cases : num [1:476] 0.0548 0.0274 0.2055 0.3288 0.0137 ...
$ percent_hospitalizations : num [1:476] 0 0 0 0.167 0 ...
$ percent_male_cases : num [1:476] 0.5 0 0.267 0.375 0 ...
$ percent_female_cases : num [1:476] 0.5 1 0.733 0.625 1 ...
4.1.0 Density and histogram plots model distribution of samples or
individuals within a population
While bar plots help to focus on proportional representation for
categorical data, both density plots and histograms can be used to
convey the frequency or distribution of values for a variable across its
specified range. When comparing distributions visualized this way, you
can usually compare up to 3 or 4 on the same plot before the data
becomes too crowded. These methods also give you a sense of your data
before moving forward with more detailed analyses. You can also identify
possible mistakes or artefacts in data collection.
How much data do I need? Density plots can be
thought of as smoothed versions of histograms which have been binned in
small intervals. Density plots of a single dimension
require a minimum of 4 samples but justifying
a KDE on a sample size that small is hard. Histograms
are recommended to have at least 30
observations to be considered useful and I would apply
this rule of thumb to KDEs as well.
Let’s pick a few age groups to plot based on
percent_cases. We’ll use data gathered from each PHU to see
if the same general trends (if any) apply regardless of PHU.
covid_demographics_total.df %>%
# Filter for just a few age groups
filter(period == "cumulative",
age_group %in% c("0 to 4","12 to 19", "20 to 39", "80+")) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = percent_cases, fill = age_group) +
theme(text = element_text(size = 20)) + # set text size
# 4. Geoms
geom_density(alpha = 0.5) + ## generate our KDE
geom_rug(aes(colour = age_group)) ## confirm our data values with a geom_rug

4.1.1 Apply a facet_*() to view KDEs separately
As you can see from above, as we start to have more and more age
groups, it may be better to separate them out in order to avoid too much
overlap. It may also be to our advantage to compare them in a more
vertical fashion. Recall that there are two layers we can choose from:
facet_wrap() and facet_grid(). They are
differentiated by the following characteristics:
facet_wrap(): Data is split based on available data
combinations of the facets parameter which can be defined
by a formula like ~var1+var2 or by using the quoting
function vars(var1, var2). In either case, the data is then
grouped by your variables. Panels are placed in a single ribbon
that wraps around based on the arguments in the ncol and
nrow parameters.
facet_grid(): Data is split based on the 1
or 2-dimensional combination of facet variables. Even
combinations for which there is no data will be displayed as empty
panels. Use the rows and cols parameters to
set the facets by using the vars() quoting function. Note
that you could further group your rows and/or columns by multiple
variables.
Note we’ll also use another parameter in both called
scales where we can determine if panels should share axis
scales or change them based on individual panel data in the y-axis
(free_y), x-axis (free_x) or both
(free),
Let’s first use facet_wrap() to generate a single-column
facet of KDEs based on the cumulative period from our data.
covid_demographics_total.df %>%
# filter for only cumulative data
filter(period == "cumulative") %>%
# Select for just the important columns
select(public_health_unit, age_group, percent_cases) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = percent_cases, fill = age_group) +
theme(text = element_text(size = 20)) + # set text size
# 4. Geoms
geom_density(alpha = 0.5) +
# 6. Facets
### 4.1.1 Add a simple facet wrap, with each age group existing in its own row
facet_wrap(~age_group,
scales="free_y",
ncol=1)
Adding missing grouping variables: `period`

4.1.2 Use facet_grid() to view both periods of our
data
If, for example we wanted to directly compare the data from both
periods (recent and cumulative), we could alter the above plot so that
our two periods are paneled beside each other. This can be accomplished
with the facet_grid() layer which will allow us to name
both a rows and cols grouping parameter.
Let’s update our figure so that we split our rows by
age_group and our columns by period.
covid_demographics_total.df %>%
# Select for just the important columns
select(period, public_health_unit, age_group, percent_cases) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = percent_cases, fill = age_group) +
theme(text = element_text(size = 20)) + # set text size
# 4. Geoms
geom_density(alpha = 0.5) +
# 6. Facets
## Use a facet_grid to panel data by multiple variables
facet_grid(rows = vars(age_group),
cols = vars(period),
scales="free") # Allow allow scales to alter between rows, and between columns.

While the above visualization is pretty clear, it certainly takes up
a lot of extra space. Perhaps there is a better way to generate this
kind of visualization?
4.2.0 Plot multiple distributions with a ridgeline plot
Ridgeline plots (sometimes called Joyplots) can generate a compact
way to show multiple distributions of numeric values across several
groups. The distributions can be shown as either histograms or density
plots with all of them aligned to the same horizontal axis. The vertical
axis is compressed slightly to generate an overlap between
categories.
To generate these visualizations we can use the ggridges
package which is an extension of ggplot2. In this case,
that means it uses the same grammar and can be added as a layer call to
geom_density_ridges(). A parameter to keep in mind:
Let’s begin by replicating our faceted plot from above which compares
the cumulative vs recent data across age groups.
covid_demographics_total.df %>%
# Select for just the important columns
select(period, public_health_unit, age_group, percent_cases) %>%
# Plot a ridgeline plot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = percent_cases, y = age_group, fill = age_group) +
theme_bw() + # Simplify the underlying theme
theme(text = element_text(size = 20)) + # set text size
theme(legend.position = "none") + # drop the legend since it's redundant
# 4. Geoms
### 4.2.0 Add a ridgeline plot instead of KDE
geom_density_ridges(alpha = 0.5) +
# 6. Facets
### 4.2.0 Make panels to display our period data
facet_grid(~period, scales = "free_x")

NA
4.3.0 Use geom_density_ridges_gradient() to fill
densities on a gradient
From above you can now see that we’ve somewhat compacted all that
dimensional data into a single plot that still clearly conveys the
difference in overall proportions for total infections within each age
group. The distributions across our categories suggest that the 20-39
age group makes up a larger proportion of overall cumulative cases
within each PHU. On the other hand, the 20-80 age ranges all appear to
have similar distributions of cases in the most recent data, although
that may be a less reliable indicator of the true case spread.
For our audience, we would need to clean up our axis titles to
clarify that these proportions are calculated independently. An
additional option with your ridgeline plots is the fill variant. To
accomplish a nicer gradient we will include a call to
scale_fill_viridis_c since our x-axis is continuous. Keep
in mind that we also cannot set the alpha transparency on
our density plots when filling with a gradient. We also have to set our
aesthetics fill to stat(x) to accomplish this
feat as well.
covid_demographics_total.df %>%
# Select for just the important columns
select(period, public_health_unit, age_group, percent_cases) %>%
# Plot a ridgeline plot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = percent_cases, y = age_group, fill = stat(x)) +
theme_bw() + # Simplify the underlying theme
theme(text = element_text(size = 20)) + # set text size
theme(legend.position = "none") + # drop the legend since it's redundant
# 3. Scaling
### 4.3.0 Use the Magma option
scale_fill_viridis_c(name="Percent of PHU", option = "C") +
# 4. Geoms
## Use a gradient version of ridges instead
geom_density_ridges_gradient() +
# 6. Facets
facet_grid(~period, # Equivalent to row = vars(period)
scales = "free_x")

5.0.0 Categorical distribution plots
Did you know the boxplot is nearly 50 years old! First invented in
the 1970s by our favourite statistician, John Wilder Tukey, we’ll dig
into how and when to use this iconic plot. While we’re here we’ll also
take a look at other categorical distribution plots. While our KDE and
ridgeline plots provide quite a bit of detail, they can also be a little
more limited in their space efficiency. The following categorical
distribution plots will perhaps provide some more information
efficiency.
5.1.0 Summarize population distributions with
geom_boxplot()
As you can see from the previous section, we comfortably fit a quite
few distributions on a ridgeline plot. From the looks of it, the 20-39
age group looks to make up a higher percent of cases across all the
PHUs. Previously this data was broken down by 10-year groupings but it
has since been amended to make larger age groups in the 20+ range.
Still, we can still explore this data a little closer.
Can we visualize the data in a more summarized form? Let’s explore
the box plot.

The dissection of a boxplot’s components shows us how it summarizes
data distribution.
Also known as the box and whisker plot, this visualization conveys
the distribution of samples within a group or population and is built
upon 5 principal values:
“Box”
median: dark line across centre of box
lower quartile: lower-bound of box
upper quartile: upper-bound of box
“Whiskers”
- lower extreme: length of lower whisker
- upper extreme: length of upper whisker
Together, the lower and upper quartiles produce the interquartile
range (IQR). The general implementation of boxplots classify any
observations 1.5 IQR above the upper quartile or below the lower
quartile as outliers of the distribution. The
characteristics of outliers can be set as parameters within the
geom_boxplot() layer. Parameters include
outlier.shape, outlier.size, and
outlier.colour.
Unlike a histogram, the minimum number of values to
generate a boxplot is 5. While you could generate a
boxplot on fewer numbers, you might not have actual whiskers! This is
definitely a great alternative when sample sizes are between 5-30 for
each population.
Historically this was a simple way to visualize summary
statistics of population while being easy to produce by hand. Of course,
with the age of computing, the production of kernel density estimates
have allowed for more diverse visualizations. This plot, however,
remains a popular format and thus is more readily understood by general
audiences.
Each compact box can take up the same space as a barplot column but
it gives much more information about the population. Let’s look at a
single aspect - percent_cases in the cumulative
period.
# Generate a basic box plot with outliers present
covid_demographics_total.df %>%
# Filter for cumulative data
filter(period == "cumulative") %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = percent_cases) +
theme(text = element_text(size = 20)) + # set text size
# 4. Geoms
geom_boxplot() ### 5.1.0 Add the boxplot geom

5.2.0 Upgrade your boxplot with some confidence intervals and
values
From the looks of it, we can confirm what we saw before in our
density plot - that the 20-39 age group generates the highest percentage
of cases across PHUs and that with increasing age, the number of
reported cases decreases as a proportion of the total.
We can add a few extra items to the plot to help us visualize the
data:
add our data points with geom_jitter() and remove
outliers from the boxplot to avoid double-plotting points.
mark a ~95% confidence interval within the shape of each box plot
with the notch parameter.
you can set a variable width for each boxplot that is based on
the number of samples used to generate the plot.
# Generate a basic box plot with outliers present
covid_demographics_total.df %>%
# Filter for cumulative data
filter(period == "cumulative") %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = percent_cases) +
theme(text = element_text(size = 20)) + # set text size
theme(legend.position = "none") +
# 4. Geoms
geom_boxplot(outlier.shape=NA, ### 5.2.0 Remove outliers
notch = TRUE) + ### 5.2.0 Add a confidence interval notch
### 5.2.0 Add datapoints
geom_jitter(aes(colour = public_health_unit))

5.3.0 geom_beeswarm takes plotting your points up to
the next level
As you can see from above the geom_jitter() layer does
add points to our boxplot by plotting the points such that they avoid
overlapping as much as possible. Points are restricted to the width of
the boxplot although this can also be adjusted to some degree with the
right parameters. geom_jitter() is native to the
ggplot2 package with some parameters that allow for a more
“random” distribution of your data points within a provided area.
The goal of the ggbeeswarm package is to generate points
that will not overlap but they can also be used to
simultaneously simulate the kernel density of your data. There are two
geoms supplied that work with the ggplot2 package to
accomplish this:
geom_beeswarm() has a number of parameters that can be
used to set their aes() mappings but also how the points
are laid out.
priority: determines the method used to perform the
point layout.
- options include: ascending, descending, density, random, and
none.
groupOnX: if TRUE then jitter is added to the x axis
(default behaviour) otherwise jitter along the y axis.
dodge.width: the amount by which different
aesthetics groups (must be a factor) will be dodged.
show.legend: determines of the this layer should be
included in the legends.
- options include:
NA (yes if aesthetics are mapped),
FALSE (never include), and TRUE (always
include)
cex: an optional parameter that can be used to help
set the spacing between values.
# Generate a basic box plot with outliers present
covid_demographics_total.df %>%
# Filter for cumulative data
filter(period == "cumulative") %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = percent_cases) +
theme(text = element_text(size = 20)) + # set text size
theme(legend.position = "none") +
# 4. Geoms
geom_boxplot(outlier.shape=NA, # Remove outliers
notch = TRUE) + # Add a confidence interval notch
### 5.3.0 Add datapoints as a beeswarm
geom_beeswarm()

5.3.1 geom_quasirandom() adds KDE structure to your
plotting
As we can see from above, the geom_beeswarm() layer adds
a little more structure to the data in a somewhat rigid way. Any
datapoints that are near each other are deliberately spaced out to
almost represent the distribution of your data. Of course, you may run
into some issues as your number of datapoints increases or as your data
range increases (see the 20 to 39 age group).
To remedy this, we can balance the visualization a bit with the
geom_quasirandom() function.
geom_quasirandom() works similarly to the
geom_beeswarm() function with emphasis on an additional
method of how the points are plotted:
method: determine the method for distributing the
points. Options include:
- quasirandom, pseudorandom: generates a KDE before plotting points in
a violin shape
- smiley, frowney: generates a KDE, then points are binned with
maximum bin values plotted on the outside or inside respectively
- tukey: plotted more as dotstrips in a box-plot style.
varwidth: if TRUE, vary the width of each group based
on the number of samples in the group
# Generate a basic box plot with outliers present
covid_demographics_total.df %>%
# Filter for cumulative data
filter(period == "cumulative") %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = percent_cases) +
theme(text = element_text(size = 20)) + # set text size
theme(legend.position = "none") +
# 4. Geoms
geom_boxplot(outlier.shape=NA, # Remove outliers
notch = TRUE) + # Add a confidence interval notch
### 5.3.1 Add datapoints as a quasirandom beeswarm
geom_quasirandom(width = 0.3)

Now our data points take a more nuanced approach with a uniform width
that shapes the data as a distribution. We’ll see this with even more
emphasis in a few sections.

When given the need to show data distribution, try the quasirandom
plotting of points over a simple beeswarm.
5.4.0 Add a third dimension to your boxplot with
fill
Is there more to the data we’ve visualized? We can add a third
dimension in a number of ways but the simplest would be to compare the
proportions of total cases vs totals hospitalizations over the course of
this pandemic. To do so, we can pivot our dataset to collapse
percent_cases and percent_hospitalizations
together into a single variable.
From there we’ll use the fill parameter to generate
different subgroups in our boxplot to make a grouped boxplot. In doing
so, we’ll also have to add the use of the
geom_quasirandom() parameters:
dodge.width: separate the data points by any
aesthetic groups that have been assigned.
width: set the maximium spread of your data points
within each grouping
alpha: set the opacity of your data points so you
can see more of the overlapping data.
Lastly, we’ll facet the plot by period so we can, yet again, compare
the cumulative data vs a more recent snapshot of the data.
covid_demographics_total.df %>%
# Select for just the important columns in our analysis
select(period, public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_hospitalizations
pivot_longer(cols=c(4:5), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# Plot the data as a grouped boxplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = percent_PHU_total,
fill = stat_group) + ### 5.4.0 Alter our fill aesthetic
theme(text = element_text(size = 20)) + # set text size
# 4. Geoms
geom_boxplot(outlier.shape=NA, notch = TRUE) + # Add the boxplot geom
# Add a quasirandom distribution of our data points
geom_quasirandom(dodge.width = 0.7, width = 0.2, alpha = 0.5) +
# 6. Facet
### 5.4.0 Produce our data based on period
facet_wrap(~period, nrow = 2, scales = "free_y")

In our boxplots, we are plotting the data in both a boxplot and
dotplot format. The shape of the dots helped to give a better sense of
PHU distribution within each age group. You can see that the data points
overlay on the box but also fall outside. Can we get the compact nature
of the boxplot while still getting the visual appeal generated by a
density plot?
5.5.0 Violin plot - the lovechild of density and boxplots
As the title says, the violin plot is a mixture of both the boxplot
and kernel density plot. It’s a miniaturized KDE that is mirrored across
its axis. It encompasses the summary information of the boxplot but in a
pear or violin-shaped distribution. To generate a
geom_violin() in ggplot, a minimum of three
values are required. To justify using a violin, I would again suggest
sticking to a similar rule of thumb of a minimum ~30
samples/observations to ensure an accurate representation of the
distribution.
The nuanced visualization of a violin plot gives much more
information than the box plot itself and most boxplots can be replaced
with a violin plot. Despite the gateway to more detailed distribution
information, this format remains less popular/familiar to scientists.
Therefore its immediate accessibility to your audience can be
limited.
An important parameter of this geom is scale: this sets
how big each violin is in comparison to each other. It accepts the
following values:
area: default value so all violins will have the
same area.
count: scale areas proportionately to
observations.
width: all violins have the same maximum width.
Total area is not the same for each.
Outliers and violins: Much like a KDE, the
theoretical distribution of a violin plot can generate some impossible
values - especially at the tails. Remember that this is a theoretical
distribution based on the data supplied. Depending on how much variation
is in your data, and how many outliers it has, it can really affect the
shape of your violin.
covid_demographics_total.df %>%
# Filter for only cumulative data
filter(period == "cumulative") %>%
# Select for just the important columns in our analysis
select(period, public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_hospitalizations
pivot_longer(cols=c(4:5), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# Plot the data as a grouped boxplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = percent_PHU_total, fill = stat_group) +
theme(text = element_text(size = 20)) + # set text size
# 3. Scaling
scale_y_continuous(limits = c(0, 0.6)) + # Set the y-axis limit
# 4. Geoms
geom_violin(scale = "width") + ## Add the violin geom
# Add a quasirandom distribution of our data points
geom_quasirandom(dodge.width = 0.85)

5.6.0 Violin plots represent distributions and boxplots summarize
them
The major advantage to the violin plot is that, by it’s nature, it is
very sensitive to the distribution that produces the density estimate.
The boxplot represents the summary information of a distribution but is
always a visual representation of a normal
distribution. There are not enough parameters supplied to represent
anything more complex!
The violin plot is not limited in that respect. Despite some of it’s
visual caveats, it can certainly detect
multi-modal data. Let’s make a toy example to
illustrate.
covid_demographics_total.df %>%
# Filter for only cumulative data
filter(period == "cumulative") %>%
# Select for just the important columns in our analysis
select(period, public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_hospitalizations
pivot_longer(cols=c(4:5), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# Plot a combined violin and boxplot to show difference in distributions
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = percent_PHU_total, y = age_group) + ### 5.6.0 Swap the x and y axes
theme_bw()+
theme(text = element_text(size = 20)) + # set text size
# 4. Geoms
geom_violin(scale = "width", colour="darkviolet") +
geom_boxplot(alpha = 0.6) +
geom_quasirandom(aes(colour = stat_group))

5.7.0 Combine violin and boxplots into the ultimate plot
From our above example, you can see that we blended a number of
geoms together. With a little working around, we can also
plot both violins and boxplots together in a multivariate setting! This
gives us the familiarity of the boxplot but also clearly displays the
theoretical distribution. Some steps to accomplish this:
- To put emphasis on the violin plots we set the
scale
paramater to “width”.
- We need to adjust some of the boxplot parameters to fit them within
the violin plots
- Some adjustments to the
aes() parameters for our geoms
to ensure our points are plotted correctly.
covid_demographics_total.df %>%
# Filter for only cumulative data
filter(period == "cumulative") %>%
# Select for just the important columns in our analysis
select(period, public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_hospitalizations
pivot_longer(cols=c(4:5), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# Plot the data as a grouped boxplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = percent_PHU_total) +
theme(text = element_text(size = 20)) + # set text size
# 3. Scaling
scale_colour_manual(values=c("black", "black"))+ # we'll need this to fix our boxplots
# 4. Data
# multi-factor violin plots but keep the width consistent
geom_violin(scale="width", aes(fill=stat_group)) +
# Boxplot but smaller width so they reside "within" the violin plot
geom_boxplot(aes(colour = stat_group), width=0.2,
position = position_dodge(width=0.9),
outlier.shape=NA) + # Remove the outliers
### 5.7.0 Add in all of the data points
geom_quasirandom(dodge.width = 0.85, aes(group=stat_group), alpha = 0.8)

Section 5.7.0 Comprehension Question: Looking at the
above code for our graph, why do you think we set the
aes(fill/group = stat_group) aesthetic individually for
some layers rather than directly in the aes()
layer?
5.8.0 Parallel coordinate plots can help visualize multivariate
date
While the above visualization shows with some clarity the total
distribution of our age groups, the messiness of outliers has invaded
into some of the violin plots themselves. An experienced eye, however
can still see that the bulk of the population centres around our
internal boxplots offset by the stretched out look of our violins. We
could, of course clean it up by removing some of the smaller PHU
populations or removing outliers ahead of time.
Suppose, however, we wanted to add more levels to our data like
percent_male_cases and percent_female_cases?
With 7 age groups represented, things would start to get very crowded.
To accommodate all of that data, you could facet it into 4 groups based
on the statistic used but this would separate the data, which looks
sharper when you can see it all on a single plot.
Organizing multiple groups, across multiple categories is the domain
of the parallel coordinate plot. The GGally package is a
ggplot2 extension with the ggparcoord function
which allows us to look simultaneously at our 3 indicators (total cases,
deaths, and hospitalizations) for each age group, linking each PHU. For
each PHU at each indicator, we will draw a line connecting all values
across the age groups (coordinates). We will colour our lines based on
the category of the indicator.
Although GGally is an extension for
ggplot2, we actually have to wrestle with our data a little
bit more and put it back into a wider format. Otherwise we can treat the
plot similarly after making it by changing themes etc.
The parameters for ggparcoord() require:
columns: a numeric vector of the location of columns
that will represent the coordinates
(categories/groups/variables).
groupColumm: the column that defines the indicator
type (stat_group) for the observations.
scale: each coordinate should theoretically have
it’s own scale but that isn’t always possible depending on the data.
Instead, the default method of scaling is to normalize points in terms
of the standard deviation of the data along that coordinate. You can,
however, choose to just use the original scale with
globalminmax if the ranges of columns aren’t too
disparate.
To generate this visualization, we’ll want to:
1. Remake our list of PHUs ordered by descending case load.
2. Pivot our data to wide-format, giving each age group it’s own
column in our data
# Generate the ordered list of PHUs by total cases
phu_by_cases <- phu_information.df %>%
slice(2:n()) %>% # drop the Ontario data
select(1, 2) %>% # Only take geographic_area and case_count
arrange(desc(case_count)) %>% # sort by descending order
select(1) %>% # grab just the PHU names
unlist() %>% # Unlist
as.character() # Convert to a character vector
# Look at the ordered list of PHUs
phu_by_cases
[1] "Toronto" "Peel" "York Region"
[4] "Ottawa" "Durham Region" "City of Hamilton"
[7] "Halton Region" "Region of Waterloo" "Windsor-Essex County"
[10] "Simcoe Muskoka" "Niagara Region" "Middlesex-London"
[13] "Wellington-Dufferin-Guelph" "Eastern Ontario" "Sudbury & Districts"
[16] "Southwestern" "Kingston, Frontenac and Lennox & Addington" "Brant County"
[19] "Lambton" "Thunder Bay" "Haliburton, Kawartha, Pine Ridge"
[22] "Haldimand-Norfolk" "Hastings Prince Edward" "Chatham-Kent"
[25] "Leeds, Grenville & Lanark" "Grey Bruce" "Huron Perth"
[28] "Peterborough" "Porcupine" "Algoma"
[31] "Northwestern" "North Bay Parry Sound" "Renfrew County"
[34] "Timiskaming"
covid_pcp.df <-
#head(covid_demographics_total.df)
covid_demographics_total.df %>%
# Filter for the top 15 PHUs by case load
filter(public_health_unit %in% phu_by_cases[1:15],
period == "cumulative"
) %>%
# Select for just the important columns
select(period, public_health_unit, age_group, percent_cases, percent_hospitalizations,
percent_male_cases, percent_female_cases) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases
# percent_hospitalizations, and percent_x_cases
pivot_longer(cols=c(4:7), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# We're going to reorder the factor ahead of time
# Probably a better way to do this but requiring more code
mutate(stat_group = factor(stat_group, levels=c("percent_hospitalizations", "percent_cases",
"percent_male_cases", "percent_female_cases"))) %>%
# Now we're going to pivot out wide to give each age_group it's own column
pivot_wider(names_from = age_group, values_from = c(percent_PHU_total)) %>%
# Fix the positioning of our factor levels
relocate('5 to 11', .after = '0 to 4')
head(covid_pcp.df)
# Generate a ggplot object
# 1. Data and Geoms
ggparcoord(covid_pcp.df,
columns=4:10,
groupColumn=3,
showPoints = TRUE,
scale="globalminmax") +
# 2. Aesthetics
theme_bw() +
theme(text = element_text(size = 20)) + # set text size
xlab("Age group") +
ylab("Percent of category by individual PHU") +
guides(colour = guide_legend(title="Category"))

5.8.1 group your data by multiple variables with the
interaction() function
Sometimes in your data you may want specific parts of your data to be
grouped together, even if you aren’t planning on using that information
directly to determine a specific aesthetic category like
size, colour or fill.
For example, our parallel coordinate plot from above, could
be reproduced directly in ggplot. As long as we have our data in the
proper long format, we can use specific variables to ensure our data is
displayed properly by using the group aesthetic.
In the case of our data, we’d like to be sure we can group our data
lines by following their age_group and data categories (ie
percent_cases, percent_hospitalizations, etc.). In our wrangled
dataframe this will fall under the stat_group variable
after pivoting longer.
The problem with this strategy, however, is that group
does not normally allow for more than one variable - like most
aesthetics. We can solve this with the interaction()
function which will allow us to name multiple factors that will
be used to formally categorize observations as a composite of those
factors.
With that in mind, let’s mock up a quick parallel coordinate plot of
our own!
covid_demographics_total.df %>%
# Filter for only cumulative data and reduce the PHU data to 15 groups
filter(public_health_unit %in% phu_by_cases[1:15],
period == "cumulative"
) %>%
# Select for just the important columns in our analysis
select(period, public_health_unit, age_group, percent_cases, percent_hospitalizations,
percent_male_cases, percent_female_cases) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_hospitalizations
pivot_longer(cols=c(4:7), names_to = "stat_group", values_to = "percent_PHU_total") %>%
ggplot() +
# 2. Aesthetics
aes(x = age_group, y = percent_PHU_total) +
# Set a basic theme
theme_bw()+
theme(text = element_text(size = 20)) + # set text size
# 4. Geoms
### 5.8.1 A line plot to join the points
geom_line(aes(group = interaction(public_health_unit, stat_group),
colour = stat_group)) +
### 5.8.1 Add the points in also to help follow the connections
geom_point(aes(colour = stat_group))

Looks nearly the same AND you don’t have to remember how to properly
format your data for using ggparcoord!
5.9.0 Explore the trends within groups after exploring data across
groups
From our above data we see some very interesting points looking at
our 60-79 and 80+ categories.
- These two groups have the highest share of hospitalizations across
all age groups.
- Conversely, these groups have lower shares of actual SARS-CoV-2
cases.
Remember all of this data represents the proportion or share of each
age group for each particular indicator. If, however, we really want to
understand which age group has the worst hospitalization or case rate,
we need to drill down into each age group.
Let’s generate one last visualization and look at the probability of
hospitalization after contracting SARS-CoV-2. We’ll calculate those
values on the cumulative data and then graph them.
Note: We are working with cumulative values across
the entire pandemic - this doesn’t give us a sense of the effect of
external effects such as vaccination rates, or differences between
variants!
# Start with our covid demographics data
covid_demographics_total.df %>%
# Ungroup the data
ungroup() %>%
# Filter for cumulative data
filter(period == "cumulative") %>%
# Just grab the columns we want to use
select(period, public_health_unit, age_group, total_cases, total_hospitalizations_count) %>%
# Generate our new calculations and save to our data frame
mutate(prob_hospitalization = total_hospitalizations_count/total_cases) %>%
# Drop any potential NA data due to division by 0
filter(complete.cases(.)) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = age_group, y=prob_hospitalization) +
theme(text = element_text(size = 20)) + # set text size
theme(legend.position = "none") +
xlab("age group") +
ylab("probability of hospitalization") +
ggtitle("Probability of hospitalization after contracting SARS-CoV-2 from multiple PHUs across age groups") +
# 4. Geoms
# Add a boxplot
geom_boxplot(varwidth=TRUE, outlier.shape=NA) +
# Add our data points
geom_quasirandom(aes(colour = age_group), size = 3, alpha = 0.7)

6.0.0 Class summary
We’ve covered a number of key plots today, including when and how to
use them. Next week we’ll revisit some of these plots and spruce them up
with extra touches that will take them that extra distance. Below you’ll
find a summary of what we’ve discussed today.
6.1.0 Summary of plots
| Scatterplot |
Good for exploring relationships between variables |
Bubbleplots add an extra dimension to your data |
| Barplot |
Present values across groups. |
Presenting proportions, small sample sizes |
|
Stack categories for extra dimension |
Does not dissect individual distributions |
| Nightingale plot |
Circular-wedge barplot, same properties as barplot |
Presenting data over unordered groups |
|
|
Visual emphasis on outer area size may mislead
reader |
| Racetrack plot |
Circular-ringed barplot, same properties as
barplot |
Looking for a more compact way to show barplots |
|
|
Calculate length by radians as outer rings are
“stretched” |
| Density plot |
Theoretical distribution of your sample data |
Minimum sample size 4 but 30 is more reliable |
|
|
Can plot up to 5 distributions on same axis |
|
|
Tails can produce “ghost” data |
| Ridgeplots |
Allows tighter visualizations of multiple
densities |
Good way to pack more KDEs into a smaller area |
|
Same properties as KDEs |
No real control of outliers |
|
|
Similar “ghost” data issues |
| Box and whisker plot |
Summarize distributions with 5 parameters |
Popular and compact presentation of simple
populations |
|
|
Minimum sample size = 5 |
|
|
Does not properly visualize multi-modal data |
| Violin plots |
Boxplot format with KDE violin shape |
Compact representation of distribution shape |
|
|
Less popular with nuanced interpretation |
|
|
Inherits “ghost” data and other properties of KDE |
|
|
DOES interpret multi-modal populations |
| Parallel coordinate plot |
Visual representation of multivariate data |
Connects trends across groups |
|
Related data can be connected linearly |
Not limited by number of samples |
|
|
Can help identify trends within multicategorical
data |
6.2.0 Weekly assignment
This week’s assignment will be found under the current lecture folder
under the “assignment” subfolder. It will include an R markdown notebook
that you will use to produce the code and answers for this week’s
assignment. Please provide answers in markdown or code cells that
immediately follow each question section.
| Code |
50% |
- Does it follow best practices? |
|
|
- Does it make good use of available packages? |
|
|
- Was data prepared properly |
| Answers and Output |
50% |
- Is output based on the correct dataset? |
|
|
- Are groupings appropriate |
|
|
- Are correct titles/axes/legends correct? |
|
|
- Is interpretation of the graphs correct? |
Since coding styles and solutions can differ, students are encouraged
to use best practices. Assignments may be rewarded for
well-coded or elegant solutions.
You can save and download the markdown notebook in its native format.
Submit this file to the the appropriate assignment section by 12:59 pm
on the date of our next class: March 28th, 2024.
6.3.0 Acknowledgements
Revision 1.0.0: created and prepared for
CSB1021H S LEC0141, 03-2021 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 1.0.1: edited and prepared for
CSB1020H S LEC0141, 03-2022 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 1.0.2: edited and prepared for
CSB1020H S LEC0141, 03-2023 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 2.0.0: Revised and prepared for
CSB1020H S LEC0141, 03-2024 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
The Center for the Analysis of Genome Evolution and Function
(CAGEF)
The Centre for the Analysis of Genome Evolution and Function (CAGEF)
at the University of Toronto offers comprehensive experimental design,
research, and analysis services in microbiome and metagenomic studies,
genomics, proteomics, and bioinformatics.
From targeted DNA amplicon sequencing to transcriptomes, whole
genomes, and metagenomes, from protein identification to
post-translational modification, CAGEF has the tools and knowledge to
support your research. Our state-of-the-art facility and experienced
research staff provide a broad range of services, including both
standard analyses and techniques developed by our team. In particular,
we have special expertise in microbial, plant, and environmental
systems.
For more information about us and the services we offer, please visit
https://www.cagef.utoronto.ca/.
LS0tDQp0aXRsZTogJycNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCiMgVGhpcyBhbGxvd3MgdGhlIGZpbGUgdG8gYmUgTElWRSBhbmQgcnVuIHdpdGhvdXQgZXJyb3JzIHN0b3BwaW5nIGl0Lg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVycm9yID0gVFJVRSkNCmBgYA0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0NBR0VGX3NlcnZpY2VzX3NsaWRlLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQoNCiMgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSDQoNCiMgTGVjdHVyZSAwMjogYGdncGxvdDJgIGFuZCBjaG9vc2luZyB0aGUgcmlnaHQgdmlzdWFsaXphdGlvbiBmb3IgeW91ciBhdWRpZW5jZSBMSVZFIEhUTUwNCg0KIyMgMC4xLjAgQW4gb3ZlcnZpZXcgb2YgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSDQoNCioqIkFkdmFuY2VkIEdyYXBoaWNzIGFuZCBEYXRhIFZpc3VhbGl6YXRpb24gaW4gUiIqKiBpcyBicm91Z2h0IHRvIHlvdSBieSB0aGUgQ2VudHJlIGZvciB0aGUgQW5hbHlzaXMgb2YgR2Vub21lIEV2b2x1dGlvbiAmIEZ1bmN0aW9uJ3MgKENBR0VGKSBiaW9pbmZvcm1hdGljcyB0cmFpbmluZyBpbml0aWF0aXZlLiBUaGlzIENTQjEwMjEgd2FzIGRldmVsb3BlZCB0byBlbmhhbmNlIHRoZSBza2lsbHMgb2Ygc3R1ZGVudHMgd2l0aCBiYXNpYyBiYWNrZ3JvdW5kcyBpbiBSIGJ5IGZvY3VzaW5nIG9uIGF2YWlsYWJsZSBwaGlsb3NvcGhpZXMsIG1ldGhvZHMsIGFuZCBwYWNrYWdlcyBmb3IgcGxvdHRpbmcgc2NpZW50aWZpYyBkYXRhLiBXaGlsZSB0aGUgZGF0YXNldHMgYW5kIGV4YW1wbGVzIHVzZWQgaW4gdGhpcyBjb3Vyc2Ugd2lsbCBiZSBjZW50cmVkIG9uIFNBUlMtQ29WMiBlcGlkZW1pb2xvZ2ljYWwgYW5kIGdlbm9taWMgZGF0YSwgdGhlIGxlc3NvbnMgbGVhcm5lZCBoZXJlaW4gd2lsbCBiZSBicm9hZGx5IGFwcGxpY2FibGUuDQoNClRoaXMgbGVzc29uIGlzIHRoZSBzZWNvbmQgaW4gYSA2LXBhcnQgc2VyaWVzLiBUaGUgYWltIGZvciB0aGUgZW5kIG9mIHRoaXMgc2VyaWVzIGlzIGZvciBzdHVkZW50cyB0byByZWNvZ25pemUgaG93IHRvIGltcG9ydCwgZm9ybWF0LCBhbmQgZGlzcGxheSBkYXRhIGJhc2VkIG9uIHRoZWlyIGludGVuZGVkIG1lc3NhZ2UgYW5kIGF1ZGllbmNlLiBUaGUgZm9ybWF0IGFuZCBzdHlsZSBvZiB0aGVzZSB2aXN1YWxpemF0aW9ucyB3aWxsIGhlbHAgdG8gaWRlbnRpZnkgYW5kIGNvbnZleSB0aGUga2V5IG1lc3NhZ2UocykgZnJvbSB0aGVpciBleHBlcmltZW50YWwgZGF0YS4NCg0KVGhlIHN0cnVjdHVyZSBvZiB0aGUgY2xhc3MgaXMgYSAqKmNvZGUtYWxvbmcgc3R5bGUqKiBpbiBSIG1hcmtkb3duIG5vdGVib29rcy4gQXQgdGhlIHN0YXJ0IG9mIGVhY2ggbGVjdHVyZSwgc2tlbGV0b24gdmVyc2lvbnMgb2YgdGhlIGxlY3R1cmUgd2lsbCBiZSBwcm92aWRlZCBmb3IgdXNlIG9uIHRoZSBbVW5pdmVyc2l0eSBvZiBUb3JvbnRvIGRhdGF0b29scyBIdWJdKGh0dHBzOi8vZGF0YXRvb2xzLnV0b3JvbnRvLmNhKSBzbyBzdHVkZW50cyBjYW4gcHJvZ3JhbSBhbG9uZyB3aXRoIHRoZSBpbnN0cnVjdG9yLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMC4yLjAgTGVjdHVyZSBvYmplY3RpdmVzDQoNClRoaXMgd2VlayB3aWxsIGRlbHZlIGludG8gc29tZSBoZWxwZnVsIHZpc3VhbGl6YXRpb24gYXZhaWxhYmxlIHRocm91Z2ggdGhlIGBnZ3Bsb3QyYCBwYWNrYWdlLiBGaXJzdCB3ZSdsbCBnbyBvdmVyIGRlY2lkaW5nIHdoaWNoIHZpc3VhbGl6YXRpb25zIGFyZSBiZXN0IGZvciB3aGF0IHdlIHdhbnQgdG8gYWNjb21wbGlzaCBhbmQgdGhlbiBleHBsb3JlIHNvbWUgb2YgdGhlc2UgaW4gbW9yZSBkZXRhaWwuDQoNCkF0IHRoZSBlbmQgb2YgdGhpcyBsZWN0dXJlIHlvdSB3aWxsIGhhdmUgY292ZXJlZCB0aGUgZm9sbG93aW5nIHRvcGljcw0KDQoxLiAgVGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MuDQoyLiAgU2NhdHRlcnBsb3RzIGFuZCB0aGVpciB2YXJpYW50cy4NCjMuICBCYXJwbG90cyBhbmQgdGhlaXIgdmFyaWFudHMuDQo0LiAgRGVuc2l0eSBwbG90cyBhbmQgdGhlaXIgdmFyaWFudHMuDQo1LiAgQ2F0ZWdvcmljYWwgcGxvdHMgYW5kIHRoZWlyIHZhcmlhbnRzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMC4zLjAgQSBsZWdlbmQgZm9yIHRleHQgZm9ybWF0IGluIFIgbWFya2Rvd24NCg0KYGdyZXkgYmFja2dyb3VuZGAgLSBhIHBhY2thZ2UsIGZ1bmN0aW9uLCBjb2RlLCBjb21tYW5kIG9yIGRpcmVjdG9yeS4gQmFja3RpY2tzIGFyZSBhbHNvIHVzZSBmb3IgaW4tbGluZSBjb2RlLlwNCippdGFsaWNzKiAtIGFuIGltcG9ydGFudCB0ZXJtIG9yIGNvbmNlcHQgb3IgYW4gaW5kaXZpZHVhbCBmaWxlIG9yIGZvbGRlclwNCioqYm9sZCoqIC0gaGVhZGluZyBvciBhIHRlcm0gdGhhdCBpcyBiZWluZyBkZWZpbmVkXA0KW2JsdWUgdGV4dF17c3R5bGU9ImNvbG9yOmJsdWUifSAtIG5hbWVkIG9yIHVubmFtZWQgaHlwZXJsaW5rDQoNCmAuLi5gIC0gV2l0aGluIGVhY2ggY29kaW5nIGNlbGwgdGhpcyB3aWxsIGluZGljYXRlIGFuIGFyZWEgb2YgY29kZSB0aGF0IHN0dWRlbnRzIHdpbGwgbmVlZCB0byBjb21wbGV0ZSBmb3IgdGhlIGNvZGUgY2VsbCB0byBydW4gY29ycmVjdGx5Lg0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWluZm99DQoqKkJsdWUgYm94OioqIEEga2V5IGNvbmNlcHQgdGhhdCBpcyBiZWluZyBpbnRyb2R1Y2VkDQo6OjoNCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC13YXJuaW5nfQ0KKipZZWxsb3cgYm94OioqIFJpc2sgb3IgY2F1dGlvbg0KOjo6DQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtc3VjY2Vzc30NCioqR3JlZW4gYm94ZXM6KiogUmVjb21tZW5kZWQgcmVhZHMgYW5kIHJlc291cmNlcyB0byBsZWFybiBQeXRob24NCjo6Og0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWRhbmdlcn0NCioqUmVkIGJveGVzOioqIEEgY29tcHJlaGVuc2lvbiBxdWVzdGlvbiB3aGljaCBtYXkgb3IgbWF5IG5vdCBpbnZvbHZlIGEgY29kaW5nIGNlbGwuIFlvdSB1c3VhbGx5IGZpbmQgdGhlc2UgYXQgdGhlIGVuZCBvZiBhIHNlY3Rpb24uDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuNC4wIExlY3R1cmUgYW5kIGRhdGEgZmlsZXMgdXNlZCBpbiB0aGlzIGNvdXJzZQ0KDQojIyMgMC40LjEgV2Vla2x5IExlY3R1cmUgYW5kIHNrZWxldG9uIGZpbGVzDQoNCkVhY2ggd2VlaywgbmV3IGxlc3NvbiBmaWxlcyB3aWxsIGFwcGVhciB3aXRoaW4geW91ciBSU3R1ZGlvIGZvbGRlcnMuIFdlIGFyZSBwdWxsaW5nIGZyb20gYSBHaXRIdWIgcmVwb3NpdG9yeSB1c2luZyB0aGlzIFtSZXBvc2l0b3J5IGdpdC1wdWxsIGxpbmtdKGh0dHBzOi8vci5kYXRhdG9vbHMudXRvcm9udG8uY2EvaHViL3VzZXItcmVkaXJlY3QvZ2l0LXB1bGw/cmVwbz1odHRwcyUzQSUyRiUyRmdpdGh1Yi5jb20lMkZjYW1vayUyRjIwMjQtMDMtQWR2X0dyYXBoaWNzX1ImdXJscGF0aD1yc3R1ZGlvJTJGJmJyYW5jaD1tYWluKS4gU2ltcGx5IGNsaWNrIG9uIHRoZSBsaW5rIGFuZCBpdCB3aWxsIHRha2UgeW91IHRvIHRoZSBbVW5pdmVyc2l0eSBvZiBUb3JvbnRvIGRhdGF0b29scyBIdWJdKGh0dHBzOi8vZGF0YXRvb2xzLnV0b3JvbnRvLmNhKS4gWW91IHdpbGwgbmVlZCB0byB1c2UgeW91ciBVVE9SaWQgY3JlZGVudGlhbHMgdG8gY29tcGxldGUgdGhlIGxvZ2luIHByb2Nlc3MuIEZyb20gdGhlcmUgeW91IHdpbGwgZmluZCBlYWNoIHdlZWsncyBsZWN0dXJlIGZpbGVzIGluIHRoZSBkaXJlY3RvcnkgYC8yMDI0LTAzLUFkdl9HcmFwaGljc19SL0xlY3R1cmVfWFhgLiBZb3Ugd2lsbCBmaW5kIGEgcGFydGlhbGx5IGNvZGVkIGBza2VsZXRvbi5SbWRgIGZpbGUgYXMgd2VsbCBhcyBhbGwgb2YgdGhlIGRhdGEgZmlsZXMgbmVjZXNzYXJ5IHRvIHJ1biB0aGUgd2VlaydzIGxlY3R1cmUuDQoNCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gZG93bmxvYWQgdGhlIFItTWFya2Rvd24gTm90ZWJvb2sgKGAuUm1kYCkgYW5kIGRhdGEgZmlsZXMgZnJvbSB0aGUgUlN0dWRpbyBzZXJ2ZXIgdG8geW91ciBwZXJzb25hbCBjb21wdXRlciBpZiB5b3Ugd291bGQgbGlrZSB0byBydW4gaW5kZXBlbmRlbnRseSBvZiB0aGUgVG9yb250byB0b29scy4NCg0KIyMjIDAuNC4yIExpdmUtY29kaW5nIEhUTUwgcGFnZQ0KDQpBIGxpdmUgbGVjdHVyZSB2ZXJzaW9uIHdpbGwgYmUgYXZhaWxhYmxlIGF0IFtjYW1vay5naXRodWIuaW9dKGh0dHBzOi8vY2Ftb2suZ2l0aHViLmlvLzIwMjQtMDMuQWR2X0dyYXBoaWNzX1IvaW5kZXguaHRtbCkgdGhhdCB3aWxsIHVwZGF0ZSBhcyB0aGUgbGVjdHVyZSBwcm9ncmVzc2VzLiBCZSBzdXJlIHRvIHJlZnJlc2ggdG8gdGFrZSBhIGxvb2sgaWYgeW91IGdldCBsb3N0IQ0KDQojIyMgMC40LjMgUG9zdC1sZWN0dXJlIFBERnMNCg0KQXMgbWVudGlvbmVkIGFib3ZlLCBhdCB0aGUgZW5kIG9mIGVhY2ggbGVjdHVyZSB0aGVyZSB3aWxsIGJlIGEgY29tcGxldGVkIHZlcnNpb24gb2YgdGhlIGxlY3R1cmUgY29kZSByZWxlYXNlZCBhcyBhIFBERiBmaWxlIHVuZGVyIHRoZSBNb2R1bGVzIHNlY3Rpb24gb2YgUXVlcmN1cy4NCg0KIyMjIDAuNC40IERhdGEgdXNlZCBpbiB0aGlzIGxlc3Nvbg0KDQpUb2RheSdzIGRhdGFzZXRzIHdpbGwgZm9jdXMgb24gc29tZSBtb3JlIE9udGFyaW8gcHVibGljIGhlYWx0aCB1bml0IGRhdGEuIFdlJ2xsIGRlZXAgZGl2ZSBhIGJpdCBtb3JlIGludG8gdGhlIGRlbW9ncmFwaGljcyBvZiBTQVJTLUNvVi0yIGluZmVjdGlvbiwgaG9zcGl0YWxpemF0aW9uLCBhbmQgc3Vydml2YWwuDQoNCiMjIyAwLjQuNC4xIERhdGFzZXQgMjogQ09WSUQtMTlfbWFwX2RhdGFfMjIwMzAzLmNzdg0KDQpSZXRyaWV2ZWQgZnJvbSB0aGUgb3BlbiBkYXRhIHNldHMgYXZhaWxhYmxlIGF0IFtQdWJsaWMgSGVhbHRoIE9udGFyaW9dKGh0dHBzOi8vd3d3LnB1YmxpY2hlYWx0aG9udGFyaW8uY2EvZW4vZGF0YS1hbmQtYW5hbHlzaXMvdXNpbmctZGF0YS9vcGVuLWRhdGEpLCB0aGlzIGRhdGEgcHJvdmlkZXMgc3VtbWFyeSBzdGF0aXN0aWMgaW5mb3JtYXRpb24gZm9yIGFsbCBQSFVzIGluIE9udGFyaW8gaW5jbHVkaW5nIHRoZSBbcG9wdWxhdGlvbiBzaXplIGZvciBlYWNoIFBIVV0oaHR0cHM6Ly93d3cucHVibGljaGVhbHRob250YXJpby5jYS9lbi9kYXRhLWFuZC1hbmFseXNpcy9pbmZlY3Rpb3VzLWRpc2Vhc2UvY292aWQtMTktZGF0YS1zdXJ2ZWlsbGFuY2UvY292aWQtMTktZGF0YS10b29sP3RhYj1tYXBzKS4NCg0KIyMjIDAuNC40LjIgRGF0YXNldCAxOiBPbnRhcmlvX2RhaWx5X2NoYW5nZV9pbl9jYXNlc19ieV9waHVfbG9uZy50c3YNCg0KVGhpcyBkYXRhIG9yaWdpbmF0ZWQgZnJvbSB0aGUgT250YXJpbyBQcm92aW5jaWFsIGdvdmVybm1lbnQgYnV0IHdhcyB0aWRpZWQgZHVyaW5nIGxlY3R1cmUgMDEgdG8gcmVmbGVjdCBhIGxvbmctZm9ybWF0IHNldCBvZiBkYXRhIGNvbXBhdGlibGUgd2l0aCBvdXIgbmVlZHMuDQoNCiMjIyAwLjQuNC4zIERhdGFzZXQgMzogT250YXJpb19hZ2Vfc2V4X0NPVklELTE5X2Nhc2VzLmNzdg0KDQpSZXRyaWV2ZWQgZnJvbSB0aGUgb3BlbiBkYXRhIHNldHMgYXZhaWxhYmxlIGF0IFtQdWJsaWMgSGVhbHRoIE9udGFyaW9dKGh0dHBzOi8vd3d3LnB1YmxpY2hlYWx0aG9udGFyaW8uY2EvZW4vZGF0YS1hbmQtYW5hbHlzaXMvdXNpbmctZGF0YS9vcGVuLWRhdGEpLCB0aGlzIGRhdGFzZXQgZm9jdXNlcyBvbiBzdW1tYXJpemluZyBbUEhVIGRhdGEgYWNyb3NzIGFnZSBhbmQgc2V4IGdyb3Vwc10oaHR0cHM6Ly93d3cucHVibGljaGVhbHRob250YXJpby5jYS9lbi9kYXRhLWFuZC1hbmFseXNpcy9pbmZlY3Rpb3VzLWRpc2Vhc2UvY292aWQtMTktZGF0YS1zdXJ2ZWlsbGFuY2UvY292aWQtMTktZGF0YS10b29sP3RhYj1hZ2VTZXgpLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMC41LjAgUGFja2FnZXMgdXNlZCBpbiB0aGlzIGxlc3Nvbg0KDQpgdGlkeXZlcnNlYCB3aGljaCBoYXMgYSBudW1iZXIgb2YgcGFja2FnZXMgaW5jbHVkaW5nIGBkcGx5cmAsIGB0aWR5cmAsIGBzdHJpbmdyYCwgYGZvcmNhdHNgIGFuZCBgZ2dwbG90MmANCg0KYHZpcmlkaXNgIGhlbHBzIHRvIGNyZWF0ZSBjb2xvci1ibGluZCBwYWxldHRlcyBmb3Igb3VyIGRhdGEgdmlzdWFsaXphdGlvbnMNCg0KYGx1YnJpZGF0ZWAgYW5kIGB6b29gIGFyZSBoZWxwZXIgcGFja2FnZXMgdXNlZCBmb3Igd29ya2luZyB3aXRoIGRhdGUgZm9ybWF0cyBpbiBSDQoNCmBnZ2JlZXN3YXJtYCwgYGdncmlkZ2VzYCBhbmQgYEdHYWxseWAgd2hpY2ggYXJlIG5ldyBwYWNrYWdlcyB3ZSBuZWVkIHRvIGluc3RhbGwuIFRoZXknbGwgaGVscCB1cyBnZW5lcmF0ZSBzb21lIG5pY2UgZ3JhcGhzLg0KDQpgYGB7cn0NCiMgLS0tLS0tLS0tLSBJbnN0YWxsIHBhY2thZ2VzIGhlcmUgLS0tLS0tLS0tLSAjDQojIGluc3RhbGwucGFja2FnZXMoImdnYmVlc3dhcm0iLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KYGBgDQoNCmBgYHtyfQ0KIyAtLS0tLS0tLS0tIFNvdXJjZSBwYWNrYWdlcyBoZXJlIC0tLS0tLS0tLS0gIw0KIyBQYWNrYWdlcyB0byBoZWxwIHRpZHkgb3VyIGRhdGENCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQojIFBhY2thZ2VzIGZvciB0aGUgZ3JhcGhpY2FsIGFuYWx5c2lzIHNlY3Rpb24NCmxpYnJhcnkocmVwcikNCmxpYnJhcnkodmlyaWRpcykNCg0KIyBOZXcgdmlzdWFsaXNhdGlvbiBwYWNrYWdlcw0KbGlicmFyeShnZ3JpZGdlcykgIyByaWRnZWxpbmUgcGxvdHMNCmxpYnJhcnkoR0dhbGx5KSAjIFBhcmFsbGVsIGNvb3JkaW5hdGUgcGxvdHMNCmxpYnJhcnkoZ2diZWVzd2FybSkNCg0KIyBwYWNrYWdlcyB1c2VkIGZvciB3b3JraW5nIHdpdGgvZm9ybWF0aW5nIGRhdGVzIGluIFINCmxpYnJhcnkobHVicmlkYXRlKSANCmxpYnJhcnkoem9vKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDEuMC4wIFdoeSBpcyBkYXRhIHZpc3VhbGl6YXRpb24gaW1wb3J0YW50Pw0KDQpIb3cgZG8gd2UgZXh0cmFjdCBtZWFuaW5nZnVsIGluc2lnaHRzIGZyb20gb3VyIGRhdGE/IElmIHlvdSBoYXZlIHByZXZpb3VzbHkgZXhwbG9yZWQgW0Fuc2NvbWJlJ3MgcXVhcnRldF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQW5zY29tYmUlMjdzX3F1YXJ0ZXQpIHlvdSdsbCBrbm93IHRoYXQsIGFzIHNjaWVudGlzdHMgYW5kIGxheSBwZW9wbGUsIHdlIGNhbiBzb21ldGltZSBiZSBvYnNlc3NlZCB3aXRoIHN1bW1hcnkgc3RhdGlzdGljcyAtIG1lYW4sIG1lZGlhbiwgbW9kZSwgc3RhbmRhcmQgZGV2aWF0aW9uLiBXaGlsZSB0aGVzZSB2YWx1ZXMgYXJlIGEgaGVscGZ1bCB3YXkgdG8gcXVpY2tseSBhc3Nlc3MgYSBwb3B1bGF0aW9uLCB0aGV5IGNhbiBiZSBmbGF3ZWQgdG8gdGhlIHBvaW50IG9mIGRlY2VwdGlvbi4gSW5zdGVhZCwgd2Ugc2hvdWxkIHRlbXBlciBvdXIgaW52ZXN0aWdhdGlvbnMgYnkgdmlzdWFsaXppbmcgb3VyIGRhdGEuIEEgZGVlcGVyIHVuZGVyc3RhbmRpbmcgb2YgeW91ciBkYXRhIHRyZW5kcyBhbmQgcG90ZW50aWFsIG1vZGVscyBjb21lcyBmcm9tIGRpc3NlY3RpbmcgYXR0cmlidXRlcyBvZiB5b3VyIGRhdGEgd2hpY2ggY2FuIGp1bXAgb3V0IG1vcmUgZWFzaWx5IHRocm91Z2ggdmlzdWFsaXphdGlvbi4NCg0KRXF1YWxseSBpbXBvcnRhbnQgaXMgdGhlIGFiaWxpdHkgdG8gY29udmV5IG91ciBmaW5kaW5ncyB0byBvdGhlcnMuIFRoZSByaWdodCB2aXN1YWxpemF0aW9ucywgd2hldGhlciBzaW1wbGlzdGljIG9yIGNvbXBsZXgsIHNob3VsZCBlZmZlY3RpdmVseSBjb21tdW5pY2F0ZSBvdXIga2V5IG1lc3NhZ2UuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjEuMCBUaGUgZ3JhbW1hciBvZiBncmFwaGljcw0KDQpPbmUgYXBwcm9hY2ggdG8gZWZmZWN0aXZlIGRhdGEgdmlzdWFsaXphdGlvbiByZWxpZXMgb24gdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MgZnJhbWV3b3JrIG9yaWdpbmFsbHkgcHJvcG9zZWQgYnkgTGVsYW5kIFdpbGtpbnNvbiAoMjAwNSkuIFRoZSBpZGVhIG9mIGdyYW1tYXIgY2FuIGJlIHN1bW1hcml6ZWQgYXMgZm9sbG93czoNCg0KLSAgICoqR3JhbW1hcioqIGlzIHRoZSBmb3VuZGF0aW9uYWwgc2V0IG9mIHJ1bGVzIHRoYXQgZGVmaW5lIHRoZSBjb21wb25lbnRzIG9mIGEgbGFuZ3VhZ2UuDQotICAgQSBsYW5ndWFnZSBpcyBidWlsdCBvbiBhIHN0cnVjdHVyZSB0aGF0IGNvbnNpc3RzIG9mICoqc3ludGF4IGFuZCBzZW1hbnRpY3MqKi4NCg0KVGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MgZmFjaWxpdGF0ZXMgdGhlIGNvbmNpc2UgZGVzY3JpcHRpb24gb2YgYW55IGNvbXBvbmVudHMgb2YgYW55IGdyYXBoaWNzLiBIYWRsZXkgV2lja2hhbSBvZiBgdGlkeXZlcnNlYCBmYW1lIGhhcyBwcm9wb3NlZCBhIHZhcmlhbnQgb24gdGhpcyBjb25jZXB0IC0gdGhlICoqKmxheWVyZWQgZ3JhbW1hciBvZiBncmFwaGljcyoqKiBmcmFtZXdvcmsuIEJ5IGZvbGxvd2luZyBhIGxheWVyZWQgYXBwcm9hY2ggb2YgZGVmaW5lZCBjb21wb25lbnRzLCBpdCBjYW4gYmUgZWFzeSB0byBidWlsZCBhIHZpc3VhbGl6YXRpb24uDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovTWFqb3JDb21wb25lbnRzR3JhbW1hckdyYXBoaWNzLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjkwMCIvPg0KDQpUaGUgTWFqb3IgQ29tcG9uZW50cyBvZiB0aGUgR3JhbW1hciBvZiBHcmFwaGljcyBieSBbRGlwYW5qYW4gU2Fya2FyXShodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vYS1jb21wcmVoZW5zaXZlLWd1aWRlLXRvLXRoZS1ncmFtbWFyLW9mLWdyYXBoaWNzLWZvci1lZmZlY3RpdmUtdmlzdWFsaXphdGlvbi1vZi1tdWx0aS1kaW1lbnNpb25hbC0xZjkyYjRlZDQxNDkpDQo6OjoNCg0KV2UgY2FuIGJyZWFrIGRvd24gdGhlIGFib3ZlIHB5cmFtaWQgYnkgdGhlIGJhc2UgY29tcG9uZW50cywgYnVpbGRpbmcgZnJvbSB0aGUgYm90dG9tIHVwd2FyZHMuDQoNCjFcLiBEYXRhOiB5b3VyIHZpc3VhbGl6YXRpb24gYWx3YXlzIHN0YXJ0cyBoZXJlLiBXaGF0IGFyZSB0aGUgZGltZW5zaW9ucyB5b3Ugd2FudCB0byB2aXN1YWxpemUuIFdoYXQgYXNwZWN0IG9mIHlvdXIgZGF0YSBhcmUgeW91IHRyeWluZyB0byBjb252ZXk/DQoNCjJcLiBBZXN0aGV0aWNzOiBhc3NpZ24geW91ciBheGVzIGJhc2VkIG9uIHRoZSBkYXRhIGRpbWVuc2lvbnMgeW91IGhhdmUgY2hvc2VuLiBXaGVyZSB3aWxsIHRoZSBtYWpvcml0eSBvZiB0aGUgZGF0YSBmYWxsIG9uIHlvdXIgcGxvdD8gQXJlIHRoZXJlIG90aGVyIGRpbWVuc2lvbnMgKHN1Y2ggYXMgY2F0ZWdvcmljYWxseSBlbmNvZGVkIGdyb3VwaW5ncykgdGhhdCBjYW4gYmUgY29udmV5ZWQgYnkgYXNwZWN0cyBsaWtlIHNpemUsIHNoYXBlLCBjb2xvdXIsIGZpbGwsIGV0Yy4NCg0KM1wuIFNjYWxlOiBkbyB5b3UgbmVlZCB0byBzY2FsZS90cmFuc2Zvcm0gYW55IHZhbHVlcyB0byBmaXQgeW91ciBkYXRhIHdpdGhpbiBhIHJhbmdlPyBUaGlzIGluY2x1ZGVzIGxheWVycyB0aGF0IG1hcCBiZXR3ZWVuIHRoZSBkYXRhIGFuZCB0aGUgYWVzdGhldGljcy4NCg0KNFwuIEdlb21ldHJpYyBvYmplY3RzOiBob3cgd2lsbCB5b3UgZGlzcGxheSB5b3VyIGRhdGEgd2l0aGluIHlvdXIgdmlzdWFsaXphdGlvbi4gV2hpY2ggYGdlb21fKmAgd2lsbCB5b3UgdXNlPw0KDQo1XC4gU3RhdGlzdGljczogYXJlIHRoZXJlIGFkZGl0aW9uYWwgc3VtbWFyeSBzdGF0aXN0aWNzIHRoYXQgc2hvdWxkIGJlIGluY2x1ZGVkIGluIHRoZSB2aXN1YWxpemF0aW9uPyBTb21lIGV4YW1wbGVzIGluY2x1ZGUgY2VudHJhbCB0ZW5kZW5jeSwgc3ByZWFkLCBjb25maWRlbmNlIGludGVydmFscywgc3RhbmRhcmQgZXJyb3IsIGV0Yy4NCg0KNlwuIEZhY2V0czogd2lsbCBnZW5lcmF0aW5nIHN1YnBsb3RzIG9mIHRoZSBkYXRhIGFkZCBhIGRpbWVuc2lvbiB0byBvdXIgdmlzdWFsaXphdGlvbiB0aGF0IHdvdWxkIG90aGVyd2lzZSBiZSBsb3N0Pw0KDQo3XC4gQ29vcmRpbmF0ZSBzeXN0ZW06IHdpbGwgeW91ciB2aXN1YWxpemF0aW9uIGZvbGxvdyBhIGNsYXNzaWMgY2FydGVzaWFuLCBzZW1pLWxvZywgcG9sYXIsIGV0Yy4gY29vcmRpbmF0ZSBzeXN0ZW0/DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjIuMCBDdW11bGF0aXZlIGNhc2Vsb2FkIGRhdGEgYnkgUEhVDQoNCkxldCdzIGxvb2sgYXQgYSBzdW1tYXJpemVkIGRhdGEgc2V0IHRoaXMgd2VlayB3aXRoIGN1bXVsYXRpdmUgY2FzZSBjb3VudHMgYWNyb3NzIGFsbCBQSFVzIGluIE9udGFyaW8uIFdoYXQgaXMgbmljZSBhYm91dCB0aGlzIGRhdGEgc2V0IGlzIHRoYXQgaXQgYWxzbyBjYXJyaWVzIHNvbWUgcG9wdWxhdGlvbiBkYXRhIHdpdGggaXQgdG8gZ2l2ZSB1cyBhIHNlbnNlIG9mIHByb3BvcnRpb25zLiBUaGlzIGRhdGEgY29tZXMgZnJvbSBhIHBlcmlvZCB3aGVyZSBjdW11bGF0aXZlIGNhc2UgY291bnRzIHN0aWxsIGhhZCBzb21lIG1lYW5pbmcgdG8gdGhlbSAoTWFyY2ggMjAyMikgc28gd2Ugd2lsbCB1c2UgaXQgdG8gaGVscCBkZW1vbnN0cmF0ZSBzb21lIG9mIHRoZSBpbmZvcm1hdGlvbiB3ZSB3YW50IHRvIHZpc3VhbGl6ZS4NCg0KU3RlcHMgd2UnbGwgdGFrZSBpbiB3b3JraW5nIHdpdGggdGhpcyBkYXRhOg0KDQoxLiAgT3BlbiB1cCB0aGUgZmlsZSBgZGF0YS9DT1ZJRC0xOV9tYXBfZGF0YV8yMjAzMDMuY3N2YCB3aXRoIGByZWFkX2NzdigpYA0KMi4gIEFkanVzdCB0aGUgY29sdW1uIG5hbWVzDQozLiAgRHJvcCB0aGUgInJlY2VudCIgZGF0YSBidXQga2VlcCB0aGUgY3VtdWxhdGl2ZSBkYXRhDQoNCkxldCdzIG9wZW4gdGhhdCB1cCB3aXRoIG91ciBmcmllbmQgYHJlYWRfY3N2KClgDQoNCmBgYHtyfQ0KIyBSZWFkIGluIFBIVV9wb3B1bGF0aW9uX2luZm9ybWF0aW9uLmNzdg0KcGh1X2luZm9ybWF0aW9uLmRmIDwtIHJlYWRfY3N2KC4uLikNCg0KIyBDaGVjayB0aGUgc3RydWN0dXJlIGFuZCBwcmV2aWV3IHRoZSBkYXRhDQpzdHIocGh1X2luZm9ybWF0aW9uLmRmKQ0KaGVhZChwaHVfaW5mb3JtYXRpb24uZGYpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCkxvb2tpbmcgYXQgdGhlIGRhdGEgaXRzZWxmLCB3ZSB3YW50IHRvIHBlcmZvcm0gdGhlIGZvbGxvd2luZyB3cmFuZ2xpbmcgYWN0aW9uczoNCg0KMVwuIFJlbmFtZSB0aGUgdmFyaWFibGUgbmFtZXMgdG8gbG93ZXIgY2FzZS4NCg0KMlwuIEluIHRoZSB2YXJpYWJsZSBuYW1lcywgcmVwbGFjZSBhbGwgb2YgdGhlIHNwYWNlcyB3aXRoIGBfYCBjaGFyYWN0ZXJzLg0KDQozXC4gVXBkYXRlIHRoZSB2YWx1ZXMgaW4gdGhlIGBHZW9ncmFwaGljIGFyZWFgIHZhcmlhYmxlIHRvIHJlbW92ZSBleGNlc3MgaW5mb3JtYXRpb24uDQoNCjRcLiBEcm9wIHRoZSB2YXJpYWJsZXMgYFJlY2VudCBDYXNlIENvdW50YCBhbmQgYFJlY2VudCBDYXNlIFJhdGVgLg0KDQo1XC4gRHJvcCBhbGwgb2YgdGhlIHZhY2NpbmF0aW9uIHZhcmlhYmxlcyAoY29udGFpbmluZyB0aGUgd29yZHMgIkNvbXBsZXRlZCIgb3IgImRvc2UiKS4gV2UnbGwgdXNlIGEgc2VsZWN0aW9uIGhlbHBlciBgbWF0Y2hlcygpYCB0byBhY2NvbXBsaXNoIHRoaXMuDQoNCjZcLiBSZW1vdmUgdGhlIHByZWZpeCB3b3JkICJDdW11bGF0aXZlIiBmcm9tIGFueSB2YXJpYWJsZSBuYW1lcy4NCg0KN1wuIFJlbmFtZSB0aGUgYEdlb2dyYXBoaWMgYXJlYWAgdmFyaWFibGUgdG8gYHB1YmxpY19oZWFsdGhfdW5pdGAuDQoNCmBgYHtyfQ0KdW5pcXVlKHBodV9pbmZvcm1hdGlvbi5kZiQnR2VvZ3JhcGhpYyBhcmVhJykNCmBgYA0KDQpgYGB7cn0NCiMgVGFrZSBvdXIgUEhVIGluZm9ybWF0aW9uIGFuZCB0aWR5IGl0IHVwIGEgYml0DQoNCnBodV9pbmZvcm1hdGlvbi5kZiAlPD4lIA0KDQogICMgUmVuYW1lIHZhcmlhYmxlcyB0byBsb3dlciBjYXNlDQogIHJlbmFtZV93aXRoKHN0cl90b19sb3dlcikgJT4lIA0KICANCiAgIyBSZXBsYWNlIHZhcmlhYmxlIG5hbWUgc3BhY2VzIHdpdGggXw0KICByZW5hbWVfd2l0aChzdHJfcmVwbGFjZV9hbGwsIHBhdHRlcm49ciIoXHMpIiwgcmVwbGFjZW1lbnQ9Il8iKSAlPiUgDQogIA0KICAjIFJlbmFtZSB0aGUgdmFsdWVzIGluIGdlb2dyYXBoaWNfYXJlYQ0KICBtdXRhdGUoZ2VvZ3JhcGhpY19hcmVhID0gc3RyX3JlbW92ZV9hbGwoLiRnZW9ncmFwaGljX2FyZWEsIA0KICAgICAgcGF0dGVybj1yIiheUHVibGljXHNIZWFsdGhcc3xcc1B1YmxpYy4qfFxzYW5kXHNEaXN0cmljdFxzLip8XHNEaXN0cmljdFxzLip8XHNIZWFsdGguKikiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgKSkgJT4lIA0KICANCiAgIyBEcm9wIHRoZSAiUmVjZW50IiBkYXRhIA0KICBzZWxlY3QoLTIsIC0zKSAlPiUgDQogIA0KICAjIFJlbW92ZSBhbnkgdmFyaWFibGVzIGNvbnRhaW5pbmcgdGhlIHdvcmRzICJDb21wbGV0ZWQiIG9yICJkb3NlIg0KICBzZWxlY3QoLi4uKSAlPiUgDQogIA0KICAjIFJlbmFtZSB0aGUgIkN1bXVsYXRpdmUiIHZhcmlhYmxlcw0KICByZW5hbWVfd2l0aChzdHJfcmVwbGFjZV9hbGwsIHBhdHRlcm49ImN1bXVsYXRpdmVfIiwgcmVwbGFjZW1lbnQgPSAiIikgJT4lIA0KICANCiAgIyBSZW5hbWUgImdlb2dyYXBoaWNfYXJlYSIgdG8gInB1YmxpY19oZWFsdGhfdW5pdCINCiAgcmVuYW1lKHB1YmxpY19oZWFsdGhfdW5pdCA9IGdlb2dyYXBoaWNfYXJlYSkNCg0KaGVhZChwaHVfaW5mb3JtYXRpb24uZGYpDQpzdHIocGh1X2luZm9ybWF0aW9uLmRmKQ0KYGBgDQoNCmBgYHtyfQ0KIyBWaWV3IHRoZSB1cGRhdGVkIFBIVSBsaXN0DQp1bmlxdWUocGh1X2luZm9ybWF0aW9uLmRmJHB1YmxpY19oZWFsdGhfdW5pdCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KV2UncmUgbm93IHJlYWR5IHRvIHN0YXJ0IHZpc3VhbGl6aW5nIG91ciBkYXRhLCBidXQgaG93IHdpbGwgd2UgZ28gYWJvdXQgZG9pbmcgaXQ/DQoNCiMjIDEuMy4wIFdoaWNoIGFzcGVjdHMgb2YgdGhlIGRhdGEgZG8gd2Ugd2lzaCB0byBoaWdobGlnaHQ/DQoNCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBbZm9sbG93aW5nIGNoYXJ0XShodHRwczovL2V4dHJlbWVwcmVzZW50YXRpb24udHlwZXBhZC5jb20vYmxvZy8yMDA2LzA5L2Nob29zaW5nX2FfZ29vZC5odG1sKSBmcm9tIGZyb20gRHIuIEFuZHJldyBWLiBBYmVsYS4NCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9BYmVsYS5DaGFydENob29zZXIucG5nP3Jhdz10cnVlIiB3aWR0aD0iOTAwIi8+DQoNCkZyb20gdGhlIGZsb3djaGFydCBhYm92ZSB3ZSdsbCBtYWlubHkgZXhwbG9yZSBsb29raW5nIGF0IHJlbGF0aW9uc2hpcHMgYW5kIGNvbXBvc2l0aW9uIHVzaW5nIG91ciBkYXRhc2V0IGFzIGEgYmFzaXMgZm9yIG91ciB2aXN1YWxpemF0aW9ucy4gV2UnbGwgY292ZXIgZGlzdHJpYnV0aW9ucyBhbmQgY29tcGFyaXNvbiB3aXRoIGEgbW9yZSBjb21wbGV4IGRhdGFzZXQgbGF0ZXIgaW4gdGhpcyBsZWN0dXJlLg0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDIuMC4wIFZpc3VhbGl6YXRpb25zIGhlbHAgdXMgaWRlbnRpZnkgcmVsYXRpb25zaGlwcyBhbmQgY29ycmVsYXRpb25zDQoNCkRlcGVuZGluZyBvbiB0aGUgbmF0dXJlIG9mIHlvdXIgZGF0YSBhbmQgaXRzIGRpbWVuc2lvbnMgdGhlcmUgYXJlIGEgbnVtYmVyIG9mIHBsb3RzIHRvIGNob29zZSBmcm9tIHRvIHJlcHJlc2VudCBhbmQgY29udmV5IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB5b3VyIHZhcmlhYmxlcy4gVGhlc2UgdmFyaW91cyBwbG90cyBjYW4gcmV2ZWFsIHRyZW5kcywgY29ycmVsYXRpb25zLCBhbmQgdWx0aW1hdGVseSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdmFyaWFibGVzLiBGb3IgaW5zdGFuY2UsIGFzIGFuIGluaXRpYWwgZm9ybSBvZiBncmFwaGljYWwgYXNzZXNzbWVudCwgc2NhdHRlcnBsb3RzIGJ1aWxkIGEgZnJhbWV3b3JrIGZvciBleHBsb3Jpbmcgb3VyIGRhdGEgZnVydGhlci4NCg0KfCBSZWxhdGlvbnNoaXAgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IFR5cGVzIG9mIGNoYXJ0cyAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IEZpbmRpbmcgY29ycmVsYXRpb25zIGluIHlvdXIgZGF0YSAgICAgICAgICAgICAgICAgICAgIHwgU2NhdHRlcnBsb3QgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQnViYmxlIGNoYXJ0ICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgTGluZXBsb3QgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgSGVhdG1hcCAgICAgICAgICAgICAgICAgICB8DQp8IFNob3dpbmcgY29ubmVjdGlvbnMgYmV0d2VlbiBncm91cHMgICAgICAgICAgICAgICAgICAgIHwgQXJjIGRpYWdyYW0gICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQ2hvcmQgZGlhZ3JhbSAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQ29ubmVjdGlvbiBtYXAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgTmV0d29yayBkaWFncmFtICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgTm9uLXJpYmJvbiBjaG9yZCBkaWFncmFtICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgVHJlZSBkaWFncmFtICAgICAgICAgICAgICB8DQp8IFNob3cgcmVsYXRpb25zaGlwcyBhbmQgY29ubmVjdGlvbnMgYmV0d2VlbiB0aGUgZGF0YSAgIHwgSGVhdG1hcCAgICAgICAgICAgICAgICAgICB8DQp8IG9yIHNob3dpbmcgY29ycmVsYXRpb25zIGJldHdlZW4gdHdvIG9yIG1vcmUgdmFyaWFibGVzIHwgTWFyaW1la2tvIENoYXJ0ICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgUGFyYWxsZWwgY29vcmRpbmF0ZXMgcGxvdCB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgUmFkYXIgY2hhcnQgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgVmVubiBkaWFncmFtICAgICAgICAgICAgICB8DQoNCkluIG91ciBmaXJzdCBsZWN0dXJlLCB3ZSBjb3ZlcmVkIHRoZSBjcmVhdGlvbiBvZiBsaW5lcGxvdHMuIEZvciB0b2RheSwgd2UnbGwgZm9jdXMgb24ganVzdCB0d28gcmVsYXRpb25hbCBwbG90czogdGhlIHNjYXR0ZXJwbG90IGFuZCBpdCdzIHZhcmlhbnQgdGhlIGJ1YmJsZWNoYXJ0LiBQYXJhbGxlbCBjb29yZGluYXRlIHBsb3RzIHdpbGwgYWxzbyBtYWtlIGFuIGFwcGVhcmFuY2UgbGF0ZXIgb24gZHVyaW5nIHRoaXMgbGVjdHVyZS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDIuMS4wIFNjYXR0ZXJwbG90cyBhbmQgYnViYmxlY2hhcnRzIGFyZSBgZ2VvbV9wb2ludCgpYCBncmFwaHMNCg0KV2hlbiB3ZSB0cnkgdG8gZXhhbWluZSByZWxhdGlvbnNoaXBzLCBvbmUgZGlyZWN0aW9uIHdlIGNhbiB0YWtlIGlzIHRvIGxvb2sgZm9yIHRyZW5kcyBieSBjb21wYXJpbmcgb25lIHZhcmlhYmxlIGFnYWluc3QgYW5vdGhlci4gRnJvbSBhbiBleHBlcmltZW50YWwgc3RhbmRwb2ludCB3ZSBjYW4gZ3JhcGggb3VyIGluZGVwZW5kZW50IHZhcmlhYmxlIG9uIHRoZSB4LWF4aXMsIGxvb2tpbmcgZm9yIGNoYW5nZXMgaW4gb3VyIGRlcGVuZGVudCB2YXJpYWJsZS9tZWFzdXJlbWVudCBvbiB0aGUgeS1heGlzLiBUaGlzIGlzIG1vc3QgZWFzaWx5IGFjY29tcGxpc2hlZCB3aGVuIGJvdGggb2YgeW91ciB2YXJpYWJsZXMgYXJlIG9uIGEgY29udGludW91cyBzY2FsZS4NCg0KV2hlbiB0cnlpbmcgdG8gc2hvdyBjb3JyZWxhdGlvbnMgaW4geW91ciBkYXRhIGJldHdlZW4gdHdvIHRvIHRocmVlIHZhcmlhYmxlcyB5b3UgY2FuIHVzZSBzY2F0dGVycGxvdHMuIFJlbWVtYmVyIHRoZXJlIGFyZSBzb21lIGxpbWl0YXRpb25zIHdoZW4gdmlzdWFsaXppbmcgbXVsdGktZGltZW5zaW9uYWwgZGF0YSBvbiBhIHR3by1kaW1lbnNpb25hbCBjYW52YXMuIE92ZXJhbGwgdGhlc2UgcGxvdHMgY2FuIGNvbnZleSB0byB5b3VyIGF1ZGllbmNlIHRoZSBwb3RlbnRpYWwgY29ycmVsYXRpb25zIGJldHdlZW4geW91ciB2YXJpYWJsZXMgb3Igc2VwYXJhdGlvbnMgYmV0d2VlbiBncm91cHMgYmFzZWQgb24gdGhlaXIgY29sb3VyIG9yIHNoYXBlLg0KDQojIyMgMi4xLjEgVXNlIHRoZSBgYWxwaGFgIHBhcmFtZXRlciB0byBoZWxwIHZpc3VhbGl6ZSBvdmVybGFwcGluZyBkYXRhIHBvaW50cw0KDQpMZXQncyBiZWdpbiB3aXRoIGEgc2NhdHRlcnBsb3QgY29tcGFyaW5nIHRoZSBudW1iZXIgb2YgY2FzZXMgdmVyc3VzIGhvc3BpdGFsaXphdGlvbnMuIFdlJ2xsIHVzZSB0aGUgb2JzZXJ2YXRpb25zIGZyb20gZWFjaCBQSFUgYXMgc2VwYXJhdGUgZGF0YXBvaW50cy4gT2Ygbm90ZSwgd2l0aGluIHRoZSBgZ2VvbV9wb2ludCgpYCBsYXllciwgd2UnbGwgYmUgdXBkYXRpbmcgdGhlIGBhbHBoYWAgcGFyYW1ldGVyIHRvIGNoYW5nZSB0aGUgdHJhbnNwYXJlbmN5IG9mIG91ciBwb2ludHMuDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtaW5mb30NCjxiPlVzaW5nIHRoZSAiYWxwaGEiIHBhcmFtZXRlcjo8L2I+IE5vdGUgdGhhdCA8Yj5gYWxwaGEgPSAxYDwvYj4gd2lsbCBtYWtlIGNvbXBsZXRlbHkgb3BhcXVlIHBvaW50cyB3aGlsZSA8Yj5gYWxwaGEgPSAwYDwvYj4gd2lsbCBtYWtlIGNvbXBsZXRlbHkgdHJhbnNwYXJlbnQgcG9pbnRzLiBBcyBwb2ludHMgYmVnaW4gdG8gb3ZlcmxhcCwgdGhleSB3aWxsIGNyZWF0ZSBpbmNyZWFzaW5nbHkgb3BhcXVlIGFyZWFzIGluIHlvdXIgdmlzdWFsaXphdGlvbi4NCjo6Og0KDQpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy53aWR0aD03fQ0KDQojIHNjYXR0ZXIgcGxvdCBvZiBjYXNlX2NvdW50IHZzIGhvc3BpdGFsaXphdGlvbnNfY291bnQNCnBodV9pbmZvcm1hdGlvbi5kZiAlPiUgDQogICMgRHJvcCB0aGUgZmlyc3Qgcm93IG9mICJPbnRhcmlvIiBkYXRhDQogIHNsaWNlKDI6bigpKSAlPiUgDQoNCiAgIyAxLiBEYXRhDQogIGdncGxvdCgpICsNCiAgICAgICMgMi4gQWVzdGhldGljcw0KICAgICAgYWVzKHg9Li4uLCB5ID0gLi4uKSArDQogIA0KICAgICAgIyBzZXQgdGV4dCBzaXplDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsNCiAgDQogICAgICAjIDQuIEdlb21zDQogICAgICAuLi4NCiAgICANCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuMS4yIFRoZSBidWJibGVwbG90IGFkZHMgYW4gb2J2aW91cyBkaW1lbnNpb25hbGl0eSB0byB5b3VyIGRhdGENCg0KVGhlIGJ1YmJsZXBsb3QsIGFzIHdlJ2xsIHNlZSwgYnJpbmdzIGFuIGFkZGl0aW9uYWwgZGltZW5zaW9uIHRvIHlvdXIgdmlzdWFsaXphdGlvbiBieSB2YXJ5aW5nIHRoZSBzaXplIG9mIHlvdXIgcG9pbnRzIGJhc2VkIG9uIGEgKHVzdWFsbHkpIGNvbnRpbnVvdXMgdmFyaWFibGUuIFRoaXMgY2FuIGhlbHAgdG8gaGlnaGxpZ2h0IHVuZGVybHlpbmcgZGF0YSB0cmVuZHMgaW4gYSBtb3JlIG9idmlvdXMgZmFzaGlvbiBmb3IgeW91ciBhdWRpZW5jZS4NCg0KVG8gYWNoaWV2ZSB0aGlzIHdlIHdpbGwgc2V0IHRoZSBgc2l6ZWAgcGFyYW1ldGVyIGluIG91ciBhZXN0aGV0aWNzIGBhZXMoKWAgbGF5ZXIuIEJ5IHNldHRpbmcgdGhlIGBzaXplYCBwYXJhbWV0ZXIgdG8gYSB2YXJpYWJsZSBpbiBvdXIgZGF0YSwgaXQgd2lsbCBhbHRlciB0aGUgc2l6ZSBvciBvdXIgZGF0YSBwb2ludHMuIFdlIHdpbGwgYXVnbWVudCB0aGUgcmVzdWx0cyBvZiB0aGlzIGJ5IHByb3ZpZGluZyBhIHNpemUgcmFuZ2UgdGhyb3VnaCB0aGUgYHNjYWxlX3NpemUoKWAgbGF5ZXIuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy53aWR0aD03fQ0KDQojIGJ1YmJsZSBwbG90IG9mIGNhc2VfY291bnQgdnMgaG9zcGl0YWxpemF0aW9uc19jb3VudA0KcGh1X2luZm9ybWF0aW9uLmRmICU+JSANCiAgIyBEcm9wIHRoZSBmaXJzdCByb3cgb2YgIk9udGFyaW8iIGRhdGENCiAgc2xpY2UoMjpuKCkpICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoKSArDQogICAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICAgIGFlcyh4PWNhc2VfY291bnQsIHkgPSBob3NwaXRhbGl6YXRpb25zX2NvdW50LCANCiAgICAgICAgICBzaXplID0gLi4uKSArICAgICAgICAgICAgICAgICAgICAgICAjIyBTZXQgdGhlIHNpemUgYWVzdGhldGljDQogIA0KICAgICAgIyBzZXQgdGV4dCBzaXplDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsNCiAgDQogICAgICAjIDMuIFNjYWxpbmcNCiAgICAgICMjIHNldCB0aGUgcmFuZ2UgZm9yIHNpemluZyBvdXIgZG90cw0KICAgICAgc2NhbGVfc2l6ZShyYW5nZSA9IC4uLiwgbmFtZSA9ICJQb3B1bGF0aW9uIChNKSIpICsgDQogIA0KICAgICAgIyA0LiBHZW9tcw0KICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMi4yLjAgU2NhbGUgeW91ciBheGVzIHRvIGhlbHAgc2VlIHlvdXIgZGF0YSBiZXR0ZXINCg0KRnJvbSBhYm92ZSB3ZSBjYW4gc2VlIGEgbG90IG9mIHZhbHVlcyBhcmUgYnVuY2hlZCB1cCB0b2dldGhlciBhdCB0aGUgbG93ZXIgZW5kIG9mIG91ciBzY2FsZXMgYW5kIGxpa2V3aXNlIHdlIGhhdmUgYSBmZXcgdmFsdWVzIHRoYXQgYXJlIGZhciBvdXQgb24gb3VyIHgveSBheGVzLiBUbyBldmVuIHRoaXMgb3V0LCB3ZSBjYW4gdHJhbnNmb3JtIG91ciBheGVzIHRvIGRpc3BsYXkgdmFsdWVzIG9uIGEgbG9nfjEwfiBzY2FsZS4gV2UgaGF2ZSB0d28gcGxhY2VzIHdoZXJlIHdlIGNhbiBhY2NvbXBsaXNoIHRoaXM6DQoNCjEuICBUcmFuc2Zvcm0geW91ciBkYXRhIGRpcmVjdGx5IGllIGBsb2coY2FzZV9jb3VudClgLiBEYXRhIHdpbGwgYmUgcGxhY2VkIG9uIGEgbG9nIHZhbHVlIHNjYWxlIGJ1dCBtYXkgaGF2ZSBsZXNzIG1lYW5pbmcgdG8gdGhlIGxheSBwZXJzb24uDQoyLiAgVHJhbnNmb3JtIHlvdXIgc2NhbGVzL3RpY2sgbWFya3MuIFRoZSBkYXRhIGlzIHVudG91Y2hlZCBhbmQgdGhlIHZhbHVlcyBzdGlsbCBoYXZlIG1lYW5pbmcgdG8geW91ciBhdWRpZW5jZSB3aXRob3V0IGhhdmluZyB0byBkbyBjb252ZXJzaW9ucyBpbiB0aGVpciBoZWFkLg0KDQpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcud2lkdGg9N30NCg0KIyBidWJibGUgcGxvdCBvZiBjYXNlX2NvdW50IHZzIGhvc3BpdGFsaXphdGlvbnNfY291bnQNCnBodV9pbmZvcm1hdGlvbi5kZiAlPiUgDQogICMgRHJvcCB0aGUgZmlyc3Qgcm93IG9mICJPbnRhcmlvIiBkYXRhDQogIHNsaWNlKDI6bigpKSAlPiUgDQoNCiAgIyAxLiBEYXRhDQogIGdncGxvdCgpICsNCiAgICAgICMgMi4gQWVzdGhldGljcw0KICAgICAgYWVzKHg9Y2FzZV9jb3VudCwgeSA9IGhvc3BpdGFsaXphdGlvbnNfY291bnQsIHNpemUgPSBwb3B1bGF0aW9uKSArDQogIA0KICAgICAgIyBzZXQgdGV4dCBzaXplDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsNCiAgDQogICAgICAjIDMuIFNjYWxpbmcNCiAgICAgIHNjYWxlX3NpemUocmFuZ2UgPSBjKDEsIDEwKSwgbmFtZSA9ICJQb3B1bGF0aW9uIChNKSIpICsgIyBzZXQgdGhlIHJhbmdlIGZvciBzaXppbmcgb3VyIGRvdHMNCiAgDQogICAgICAjIyBTZXQgdGhlIHggYW5kIHktYXhpcyB0byBsb2cgc2NhbGVzDQogICAgICAuLi4gKyANCiAgICAgIC4uLiArIA0KICANCiAgICAgICMgNC4gR2VvbXMNCiAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCkZyb20gb3VyIGJ1YmJsZXBsb3QgaXQgaXMgY2xlYXIgdGhhdCByaXNpbmcgY2FzZSBjb3VudHMgYXJlIHRpZWQgdG8gaW5jcmVhc2luZyBwb3B1bGF0aW9uIHNpemUuIFRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBvdXIgbG9nLXRyYW5zZm9ybWVkIGRhdGEgYXBwZWFycyB0byBiZSBsaW5lYXIuDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCioqSG93IGRvIHlvdSBpbnRlcnByZXQgbG9nLXRyYW5zZm9ybWVkIGRhdGE/OioqIEFmdGVyIGxvZy10cmFuc2Zvcm1pbmcgYm90aCBheGVzIGl0IGxvb2tzIGxpa2UgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG92ZXJhbGwgY2FzZXMgYW5kIGhvc3BpdGFsaXphdGlvbnMgaXMgbGluZWFyLiBXaGlsZSB0aGUgdHJhbnNmb3JtYXRpb24gaGFzIGRlZmluaXRlbHkgaW1wcm92ZWQgdGhlIHZpc3VhbGl6YXRpb24sIGl0IGhhcyBtYWRlIHRoZSBpbnRlcnByZXRhdGlvbiBhIGxpdHRsZSBoYXJkZXIuIE92ZXJhbGwsIGluIHRoaXMgc2l0dWF0aW9uLCB5b3UnbGwgZWl0aGVyIHdhbnQgdG8gbW9kZWwgYWdhaW5zdCB0aGUgdW50cmFuc2Zvcm1lZCBkYXRhIG9yIGRvIHRoZSBtYXRoIGNvcnJlY3RseSBhbmQgcmVjb2duaXplIHRoYXQgdGhlIHJlbGF0aW9uc2hpcCBpcyBhIHBvd2VyIGZ1bmN0aW9uOg0KOjo6DQoNCmBgYHs9dGV4fQ0KXGJlZ2lue2VxdWF0aW9uKn0NCiAgICBcYmVnaW57YWxpZ25lZH0gIA0KXHRleHR7bG9nfSB5ICY9IEIgKyBhIFx0ZXh0e2xvZ30geCAmXHRleHR7d2hlcmUgQiBpcyB0aGUgaW50ZXJjZXB0IG9mIHRoZSB2ZXJ0aWNhbCBheGlzIGFuZCBhIGlzIHRoZSBzbG9wZX1cXFsxMHB0XQ0KeSAmPSAxMF57KEIgKyBhIFx0ZXh0e2xvZ30geCl9XFxbMTBwdF0NCnkgJj0gMTBee0J9IDEwXnthIFx0ZXh0e2xvZ30geH1cXFsxMHB0XQ0KeSAmPSAxMF57Qn0geF57YX0gJlx0ZXh0e1JlY2FsbCB0aGF0IH0geSBcdGV4dHtsbn0geCA9IHhee3l9XFxbMTBwdF0NCiAgICBcZW5ke2FsaWduZWR9DQpcZW5ke2VxdWF0aW9uKn0NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAzLjAuMCBEaXN0cmlidXRpb24gcGxvdHMgZGlzcGxheSBmcmVxdWVuY3kgYW5kIHNwcmVhZCBhY3Jvc3MgZ3JvdXBzIG9yIGludGVydmFscw0KDQpNYW55IHN0dWRlbnRzIGFyZSBmYW1pbGFyIHdpdGggdGhlIGNsYXNzaWMgYmFyIGNoYXJ0LiBUaHVzIGZhciwgd2UndmUgYWxyZWFkeSB1c2VkIGl0IHRvIHNvbWUgZXh0ZW50IHRvIGxvb2sgYXQgb3VyIGxhc3QgbGVjdHVyZSdzIFBIVSBjYXNlIGRhdGEuIFdoZW4gY29tcGFyaW5nIGdyb3VwcyBvciBwb3B1bGF0aW9ucyBmb3IgZGlmZmVyZW5jZXMgaW4gdGhlaXIgZGlzdHJpYnV0aW9uLCBob3dldmVyLCB0aGUgYmFyIGNoYXJ0IGZhbGxzIHF1aXRlIHNob3J0IG9mIHRoZSBpZGVhbC4NCg0KV2hlbiBjb21wYXJpbmcgZGlzdHJpYnV0aW9ucywgd2UgYWdhaW4gYXJlIG9mdGVuIGNvbmNlcm5lZCB3aXRoIHN1bW1hcnkgc3RhdGlzdGljcyAtIG1lYW4sIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBjb25maWRlbmNlIGludGVydmFscy4gVGhlIG5hdHVyZSBvZiBvdXIgZGF0YSwgaG93ZXZlciwgaXMgb2Z0ZW4gbm90IGNvbnRpbnVvdXMgYWNyb3NzIGFuIGVudGlyZSB5LWF4aXMgaW50ZXJ2YWwgYXMgYSBiYXIgY2hhcnQgbWF5IHN1Z2dlc3QgYnV0IHJhdGhlciBvdXIgcG9wdWxhdGlvbiBpcyB0aGUgcmVzdWx0IG9mIHZhbHVlcyBjZW50cmVkLCB3aXRoIHNvbWUgdmFyaWFuY2UsIGFyb3VuZCBhIG1lYW4gdmFsdWUuDQoNCk92ZXIgdGhlIHJlbWFpbmRlciBvZiB0aGUgbGVjdHVyZSB3ZSB3aWxsIGNvbXBhcmUgYW5kIGNvbnRyYXN0IDQgdHlwZXMgb2YgZGlzdHJpYnV0aW9uIHBsb3RzIGZvciB0aGVpciByZWxldmFuY2UgYW5kIHdoZW4gdGhleSBtYXkgYmUgbW9zdCB1c2VmdWwgdG8gdmlzdWFsaXplIG91ciBkYXRhLiBXaGVuIGFwcGxpY2FibGUgdG8gb3VyIGRhdGEgdmlzdWFsaXphdGlvbnMsIHdlIHdpbGwgYWxzbyBkaXNwbGF5IGFsbCBvZiBvdXIgZGF0YSBwb2ludHMgdG8gYmV0dGVyIGlsbHVzdHJhdGUgb3VyIGRpc3RyaWJ1dGlvbiB2ZXJzdXMgdGhlIHZpc3VhbGl6YXRpb24uIFdlIHdpbGwgZXhhbWluZToNCg0KMS4gIEJhciBjaGFydHMgb3IgQmFycGxvdHMNCjIuICBEZW5zaXR5IHBsb3RzDQozLiAgQm94IGFuZCBXaGlza2VyIHBsb3RzDQo0LiAgVmlvbGluIHBsb3RzDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAzLjEuMCBUaGUgYmFycGxvdCB1c2VzIHRoZSBgZ2VvbV9iYXIoKWAgb3IgYGdlb21fY29sKClgIHRvIGRpc3BsYXkgZGF0YQ0KDQpBcyB3ZSBoYXZlIGFscmVhZHkgc2VlbiwgdGhlIGBnZW9tX2JhcigpYCBsYXllciBjYW4gYmUgdXNlZCB0byBkaXNwbGF5IG91ciBkYXRhIGluIG11bHRpcGxlIHdheXMuIFRoZSBoZWlnaHQgb2YgdGhlIGJhciBpcyBwcm9wb3J0aW9uYWwgdG8gZWl0aGVyOg0KDQoxXC4gdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgb2YgZWFjaCBncm91cCBvcg0KDQoyXC4gdGhlIHN1bSBvZiB3ZWlnaHRzIGFwcGxpZWQgYnkgYSB3ZWlnaHQgYWVzdGhldGljDQoNCkluIG91ciAqKkxlY3R1cmUgMDEqKiBleGFtcGxlcyB3ZSBhY3R1YWxseSB1c2VkIHRoZSB2YWx1ZSBvZiBvdXIgb2JzZXJ2YXRpb25zIHRvIGRldGVybWluZSBoZWlnaHQgYW5kIHByb3BvcnRpb25zIG9mIG91ciBncm91cHMgYnkgc2V0dGluZyB0aGUgcGFyYW1ldGVyIGBzdGF0PSJpZGVudGl0eSJgLiBUaGlzIHdhcyB0aGUgYXBwbGljYXRpb24gb2YgYSB3ZWlnaHRpbmcgYmFzZWQgb24gdGhlIHZhbHVlcyBvZiB0aGUgeS1heGlzIHZhcmlhYmxlIHdlIGNob3NlLg0KDQpBIHNpbXBsZXIgd2F5IHRvIGFjY29tcGxpc2ggdGhpcyBlZmZlY3QgaXMgd2l0aCBgZ2VvbV9jb2woKWAsIHdoaWNoIGFscmVhZHkgdXNlcyBgc3RhdF9pZGVudGl0eSgpYCBieSBkZWZhdWx0IHRvIGNhbGN1bGF0ZSBwcm9wb3J0aW9ucy4NCg0KfCAgICBnZW9tICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgUmVxdWlyZW1lbnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS06fDotLS0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IGdlb21fYmFyKCkgfCBDb3VudHMgKioqb2JzZXJ2YXRpb25zKioqIGluIHlvdXIgZGF0YSBhbmQgKGJ5IGRlZmF1bHQpIGRldGVybWluZXMgaGVpZ2h0IGFzIGEgcHJvcG9ydGlvbiBvZiB0b3RhbCAoYnkgZGVmYXVsdCkgfCBPbmx5IGFjY2VwdHMgeCBPUiB5IHBhcmFtZXRlciBpbiBgYWVzKClgICAgIHwNCnwgZ2VvbV9jb2woKSB8IFVzZXMgeS1heGlzIHZhbHVlcyBhcyB0aGUgaGVpZ2h0IG9mIGVhY2ggYmFyLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IFJlcXVpcmVzIGJvdGggeCBBTkQgeSBwYXJhbWV0ZXJzIGluIGBhZXMoKWAgfA0KDQpCb3RoIG9mIHRoZXNlIHBsb3RzIHVzZSBgcG9zaXRpb249InN0YWNrImAgYnkgZGVmYXVsdCBhbmQgcHJvcG9ydGlvbnMgb2YgaGVpZ2h0IG1hdGNoIG9ic2VydmF0aW9ucyBvciBzdW1zIGZvciBtdWx0aXBsZSB2YWx1ZXMgc2hhcmluZyB0aGUgc2FtZSBgeGAgcG9zaXRpb24uIFN1Y2ggaW5zdGFuY2VzIGNhbiBiZSBkaXNwbGF5ZWQgaW5kZXBlbmRlbnRseSB1c2luZyBgcG9zaXRpb249ImRvZGdlImAgb3IgYHBvc2l0aW9uPWRvZGdlMmAuIFRoaXMgaXMgb25seSBoZWxwZnVsLCBob3dldmVyLCB3aGVuIHRoZSBudW1iZXIgb2YgYHhgIHZhbHVlcyAoaWUgY2F0ZWdvcmllcykgaXMgbG93ZXIsIG90aGVyd2lzZSB0aGUgZ3JhcGggYmVjb21lcyBjcm93ZGVkLg0KDQpMZXQncyBiZWdpbiB3aXRoIGEgc2ltcGxlIGJhcnBsb3Qgb2YgU0FSUy1Db3YtMi1yZWxhdGVkIGRlYXRocyBhY2N1bXVsYXRlZCBvdmVyIHRoZSBjb3Vyc2Ugb2YgdGhlIHBhbmRlbWljLiBVc2luZyBgcGh1X2luZm9ybWF0aW9uLmRmYCBhcyBhIGRhdGEgc291cmNlIHdlIHdpbGwgY29udmV5IHRoZSBkaXN0cmlidXRpb24gb2YgdG90YWwgZGVhdGhzIHBlciBQSFUgdXNpbmcgdGhlIGBnZW9tX2NvbCgpYCBsYXllci4NCg0KUmVjYWxsIHRoZSBmaXJzdCByb3cgb2Ygb3VyIGRhdGEgaXMgIk9udGFyaW8iLCByZXByZXNlbnRpbmcgdGhlIHRvdGFsIHZhbHVlcyBmb3IgZWFjaCB2YXJpYWJsZSBhcyBhIHN1bSBvZiB0aGUgb3RoZXIgUEhVcy4gV2UnbGwgcmVtb3ZlIHRoYXQgZnJvbSBvdXIgZGF0YSB1c2luZyBgc2xpY2UoKWAgYmVmb3JlIHBhc3NpbmcgaXQgb24gdG8gYGdncGxvdCgpYC4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBiYXJwbG90IG9mIGRlYXRoIGNvdW50cyBhY3Jvc3MgUEhVcw0KcGh1X2luZm9ybWF0aW9uLmRmICU+JSANCiAgIyBSZW1vdmUgdGhlIHRvcCByb3cgKE9udGFyaW8gZGF0YSkNCiAgc2xpY2UoMjpuKCkpICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICAgIyAyLiBBZXN0aGV0aWNzDQogICAgICBhZXMoeCA9IHB1YmxpY19oZWFsdGhfdW5pdCwgeSA9IC4uLikgKw0KICANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdCA9IDEsIHZqdXN0PTAuNSkpICsgIyBBZGp1c3Qgb3VyIHgtYXhpcyB0ZXh0DQogIA0KICAgICAgIyA0LiBHZW9tcw0KICAgICAgLi4uICAgICMjIEFkZCBvdXIgYmFycw0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4xLjEgVXNlIGByZW9yZGVyKClgIHRvIHNvcnQgeW91ciBkYXRhIGFzIHlvdSBwbG90IGl0DQoNCkluIHRoZSBjYXNlIG9mIGBwaHVfaW5mb3JtYXRpb24uZGZgIHdlIGNhbiBkbyBhIGxpdHRsZSBiZXR0ZXIgYnkgb3JkZXJpbmcgb3VyIHgtYXhpcyBvZiBQSFVzIGJ5ICoqdG90YWwgcG9wdWxhdGlvbioqIG9yICoqZGVhdGggY291bnRzKiouIFNpbmNlIG91ciBkYXRhIHRhYmxlIGlzIHF1aXRlIHNpbXBsZSBhbmQgb25seSBhIHNpbmdsZSBvYnNlcnZhdGlvbiBmb3IgZWFjaCBQSFUgb2NjdXJzLCB0aGVyZSBhcmUgYSBudW1iZXIgb2Ygd2F5cyB0byBhY2NvbXBsaXNoIGEgc29ydDoNCg0KMS4gIENvbnZlcnQgYHBvcHVsYXRpb25gIHRvIGEgZmFjdG9yIGFuZCB1c2UgYGZjdF9yZW9yZGVyKClgIHRvIHNvcnQgb3VyIGZhY3RvcnMuDQoyLiAgU29ydCB0aGUgZW50aXJlIHRhYmxlIHVzaW5nIGBhcnJhbmdlKClgIGJlZm9yZSBwbG90dGluZy4NCjMuICBVc2UgdGhlIGByZW9yZGVyKClgIGZ1bmN0aW9uIHdoaWxlIGdlbmVyYXRpbmcgdGhlIHBsb3QuDQoNCkxldCdzIHNvcnQgb3VyIGRhdGEgYnkgUEhVIHBvcHVsYXRpb24gc2l6ZSBpbiBvdXIgbmV4dCBleGFtcGxlIHVzaW5nIHRoZSBSIGJhc2UgZnVuY3Rpb24gYHJlb3JkZXIoKWAgKndpdGhpbiogb3VyIGBnZ3Bsb3QoKWAgY2FsbC4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBiYXJwbG90IHVzaW5nIHRoZSByZW9yZGVyIGZ1bmN0aW9uDQpwaHVfaW5mb3JtYXRpb24uZGYgJT4lIA0KICAjIFJlbW92ZSB0aGUgdG9wIHJvdyAoT250YXJpbyBkYXRhKQ0KICBzbGljZSgyOm4oKSkgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICAgICMjIyAzLjEuMSBXZSBtdXN0IGNob29zZSBhIHZhcmlhYmxlIHRvIHNvcnQgYnkgaW4gdGhlIHJlb3JkZXIgZnVuY3Rpb24gLSB1c2UgcG9wdWxhdGlvbg0KICAgICAgYWVzKHggPSAuLi4pLCANCiAgICAgICAgICB5ID0gZGVhdGhzX2NvdW50KSArDQogIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArICMgc2V0IHRleHQgc2l6ZQ0KICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0ID0gMSwgdmp1c3Q9MC41KSkgKyAjIEFkanVzdCBvdXIgeC1heGlzIHRleHQNCiAgDQogICAgICAjIDQuIEdlb21zDQogICAgICBnZW9tX2NvbCgpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDMuMi4wIEJhcnBsb3RzIGNhbiBoZWxwIGd1aWRlIHRoZSBleWUNCg0KRnJvbSBhYm92ZSwgdXNpbmcgdGhlIGJhcGxvdHMgd2UgY2FuIHF1aWNrbHkgZ2F1Z2UgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBQSFVzIGFzIHRoZXkgZ2V0IHNvcnRlZCBieSBwb3B1bGF0aW9uLiBUaGVyZSdzIG5vdCBtdWNoIG5lZWQgdG8gY29sb3VyIGJ5IHBvcHVsYXRpb24gYXMgd2VsbCBidXQgaXQgZG9lcyBhZGQgc29tZSBlbXBoYXNpcyB0byBUb3JvbnRvIGFzIGEgbGFyZ2VyIHBvcHVsYXRpb24gYW5kIGZ1cnRoZXIgZW5mb3JjZXMgdGhlIGlkZWEgdGhhdCB3ZSBoYXZlIHNvcnRlZCBieSBwb3B1bGF0aW9uIHNpemUuDQoNCldoYXQgYXJlIHdlIG1pc3NpbmcgZnJvbSB0aGlzIHZpc3VhbGl6YXRpb24gdGhhdCB3b3VsZCBoZWxwIHRoZSByZWFkZXI/IEl0IHdvdWxkIGJlIG5pY2UgdG8ga25vdyBqdXN0IGhvdyBtdWNoIHZhcmlhdGlvbiB0aGVyZSBpcyBpbiBwb3B1bGF0aW9uIHNpemUsIGVzcGVjaWFsbHkgaW4gdGhlIGZpcnN0IFx+MTIgUEhVcy4gSG93IG11Y2ggYmlnZ2VyIGlzIFBlZWwgdmVyc3VzIHRoZSBSZWdpb24gb2YgV2F0ZXJsb28/DQoNCiMjIyAzLjIuMSBVc2UgdGhlIGBmaWxsYCBwYXJhbWV0ZXIgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiBncm91cHMNCg0KT25lIHNpbXBsZSB3YXkgdG8gZW5oYW5jZSBvdXIgYmFycGxvdCBpcyB0aHJvdWdoIHRoZSB1c2Ugb2YgZmlsbCBjb2xvdXIuIEJ5IHNldHRpbmcgdGhlIGBmaWxsYCBwYXJhbWV0ZXIgdG8gYHBvcHVsYXRpb25gIHdlIGNhbiBzaGFkZSBlYWNoIGJhciBiYXNlZCBvbiB0aGUgcG9wdWxhdGlvbiBzaXplIG9mIGVhY2ggUEhVLiBMZXQncyB0YWtlIGEgbG9vay4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBiYXJwbG90IHVzaW5nIHRoZSByZW9yZGVyIGZ1bmN0aW9uDQpwaHVfaW5mb3JtYXRpb24uZGYgJT4lIA0KICAjIFJlbW92ZSB0aGUgdG9wIHJvdyAoT250YXJpbyBkYXRhKQ0KICBzbGljZSgyOm4oKSkgJT4lIA0KICAjIFdlIG11c3QgY2hvb3NlIGEgdmFyaWFibGUgdG8gc29ydCBieSBpbiB0aGUgcmVvcmRlciBmdW5jdGlvbiAtIHVzZSBwb3B1bGF0aW9uDQogIA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAgICMgMi4gQWVzdGhldGljcw0KICAgICAgYWVzKHggPSByZW9yZGVyKHB1YmxpY19oZWFsdGhfdW5pdCwgLXBvcHVsYXRpb24pLCB5ID0gZGVhdGhzX2NvdW50LCBmaWxsID0gcG9wdWxhdGlvbikgKw0KICANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdCA9IDEsIHZqdXN0PTAuNSkpICsgIyBBZGp1c3Qgb3VyIHgtYXhpcyB0ZXh0DQogIA0KICAgICAgIyMjIDMuMi4xIEFkanVzdCB0aGUgZmlsbCBsZWdlbmRzDQogICAgICBndWlkZXMoZmlsbCA9IC4uLikgKw0KICANCiAgICAgICMgNC4gR2VvbXMNCiAgICAgIGdlb21fY29sKCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMi4yIEFkZCBleHRyYSBgZ2VvbV8qYCBsYXllcnMgdG8gbW9yZSBhY2N1cmF0ZWx5IHJlZmxlY3QgdmFsdWVzDQoNCkFzIHlvdSBjYW4gc2VlIGZyb20gYWJvdmUsIHVubGVzcyB5b3UgaGF2ZSBhIHZlcnkgZ29vZCBleWUgZm9yIHNoYWRlcywgdGhlcmUgaXNuJ3QgbXVjaCB0byBoZWxwIHVzIGRpc3Rpbmd1aXNoIGJldHdlZW4gcG9wdWxhdGlvbnMgYWdhaW4uIFdlIGNhbiBzdGlsbCBzZWUgdGhhdCBUb3JvbnRvIGhhcyBjbG9zZXIgdG8gbW9yZSB0aGFuIDNNIHJlc2lkZW50cyBidXQgb3RoZXIgdGhhbiB0aGF0LCB3ZSBhZ2FpbiBoYXZlIHNvbWUgaXNzdWVzIHdpdGggZGlzY3JpbWluYXRpbmcgYmV0d2VlbiBwb3B1bGF0aW9ucy4NCg0KSW5zdGVhZCBvZiBmaWxsIGNvbG91ciwgd2UgY2FuIGFkZCBhIGxpdHRsZSBkZXRhaWwgYnkgbGF5ZXJpbmcgYW4gYWRkaXRpb25hbCBnZW9tLiBJbiB0aGlzIGNhc2UsIHdlIHdpbGwgdXNlIGBnZW9tX3BvaW50KClgIHRvIGFkZCB0aGUgcG9wdWxhdGlvbiBvZiBlYWNoIFBIVSAqYWZ0ZXIqIHNjYWxpbmcgaXQgZG93biB0byAxLzEwMDB0aC4NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1pbmZvfQ0KKipXaHkgc2NhbGUgcG9wdWxhdGlvbiBieSAxLzEwMDB0aD8qKiBXaHkgZGlkIHdlIGNob29zZSB0byBzY2FsZSB0aGUgcG9wdWxhdGlvbiBzaXplIG9mIGVhY2ggUEhVIHRvIDE6MTAwMD8gVGhpcyBpcyBtb3JlIG9mIGEgZGVjaXNpb24gYmFzZWQgb24gd2hhdCB3ZSBrbm93IGFib3V0IHRoZSBkYXRhIGFscmVhZHkuIFdlIGFscmVhZHkga25vdyBvdXIgeS1heGlzICoqZGVhdGhzX2NvdW50KiogZGF0YSBzY2FsZXMgZnJvbSAwIHRvIDQwMDAuIEtub3dpbmcgdGhlIHBvcHVsYXRpb25zIGNhbiBzY2FsZSBmcm9tIDAgdG8gM00sIGl0IG1ha2VzIHNlbnNlIHRvIHRyeSBhbmQgZ2V0IG91ciB2YWx1ZXMgYWxvbmcgdGhlIHNhbWUgc2NhbGUuDQo6OjoNCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBiYXJwbG90IHVzaW5nIHRoZSByZW9yZGVyIGZ1bmN0aW9uDQpwaHVfaW5mb3JtYXRpb24uZGYgJT4lIA0KICAjIFJlbW92ZSB0aGUgdG9wIHJvdyAoT250YXJpbyBkYXRhKQ0KICBzbGljZSgyOm4oKSkgJT4lIA0KICAjIFdlIG11c3QgY2hvb3NlIGEgdmFyaWFibGUgdG8gc29ydCBieSBpbiB0aGUgcmVvcmRlciBmdW5jdGlvbiAtIHVzZSBwb3B1bGF0aW9uDQogIA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAgICMgMi4gQWVzdGhldGljcw0KICAgICAgYWVzKHggPSByZW9yZGVyKHB1YmxpY19oZWFsdGhfdW5pdCwgLXBvcHVsYXRpb24pLCB5ID0gZGVhdGhzX2NvdW50KSArDQogIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArICMgc2V0IHRleHQgc2l6ZQ0KICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0ID0gMSwgdmp1c3Q9MC41KSkgKyAjIEFkanVzdCBvdXIgeC1heGlzIHRleHQNCiAgDQogICAgICBndWlkZXMoc2l6ZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUG9wdWxhdGlvblxuc2l6ZSBwZXJcbjEwMDAiKSwgDQogICAgICAgICAgICAgY29sb3VyID0gIm5vbmUiKSArDQogIA0KICAgICAgIyA0LiBHZW9tcw0KICAgICAgZ2VvbV9jb2woKSArDQogIA0KICAgICAgIyMjIDMuMi4yIEFkZCBhIGdlb21fcG9pbnQgdG8gcmVwcmVzZW50IHBvcHVsYXRpb24gc2l6ZQ0KICAgICAgZ2VvbV9wb2ludChhZXMoeT0uLi4sIHNpemU9MiwgY29sb3VyPSJyZWQiKSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMi4zIFRoZSBsb2xsaXBvcCBwbG90OiBhIHN3ZWV0IHR3aXN0IG9uIHRoZSBiYXJwbG90Lg0KDQpOb3cgd2UgaGF2ZSBtb3JlIGNsYXJpdHkgb24gcG9wdWxhdGlvbnMgc2l6ZXMgYmV0d2VlbiBQSFVzLCBhbnN3ZXJpbmcgb3VyIG9yaWdpbmFsIHF1ZXN0aW9uIHRoYXQgUGVlbCByZWdpb24gaGFzIG5lYXJseSBvbmUgbWlsbGlvbiBtb3JlIGluaGFiaXRhbnRzIHRoYW4gdGhlIFJlZ2lvbiBvZiBXYXRlcmxvby4gVG8gcHVibGlzaCB0aGlzIGZpZ3VyZSB3ZSB3b3VsZCB3YW50IHRvIGZpeCBhIGZldyBhZGRpdGlvbmFsIHRoaW5ncyBsaWtlIHRoZSBsZWdlbmQgcHJlc2VudGF0aW9uLCB0aGUgYXhpcyBuYW1lcyBhbmQgdGhlaXIgdGl0bGVzLiBXZSdsbCBkcmlsbCBpbnRvIHRoZXNlIGlkZWFzIG1vcmUgbmV4dCBsZWN0dXJlIQ0KDQpPbiB0aGlzIHNhbWUgdG9waWMsIGxldCdzIHZpc2l0IG9uZSBsYXN0IHBsb3QgdGhhdCBjYW4gZ2l2ZXMgdXMgc29tZSBwaWVjZXMgZnJvbSB0aGUgdHdvIHZhcmlhbnRzIG9mIGJhcnBsb3RzIHdlJ3ZlIHVzZWQuIFRoZSBsb2xsaXBvcCBncmFwaCBjbGFyaWZpZXMgdGhhdCB4LWF4aXMgdmFsdWVzIGFyZSBtb3JlIHNpbmd1bGFyIGluIG5hdHVyZSByYXRoZXIgdGhhbiBzcGFubmluZyBhIHJhbmdlIHdoaWxlIHN0aWxsIHZpc3VhbGx5IGNvbm5lY3RpbmcgdGhvc2UgeS1heGlzIHZhbHVlcyB0byB0aGVpciB4LWF4aXMgY2F0ZWdvcmllcy4NCg0KSXQgbG9va3MgdmVyeSBtdWNoIGxpa2UgaXQgc291bmRzLCBhbmQgd2UnbGwgYWRkIGFuIGV4dHJhIHR3aXN0IHRvIG91cnMgYnkgc2V0dGluZyB0aGUgcG9pbnQgc2l6ZSB0byB0aGUgcG9wdWxhdGlvbiBzaXplLCBnaXZpbmcgaXQganVzdCBhIGJpdCBtb3JlIG9mIGluZm9ybWF0aW9uYWwgZGltZW5zaW9uLiBUbyBhY2NvbXBsaXNoIHRoaXMgdmlzdWFsaXphdGlvbiB3ZSdsbCBjb21iaW5lIGEgYGdlb21fcG9pbnQoKWAgd2l0aCBhIGBnZW9tX3NlZ21lbnQoKWAuDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCioqQWVzdGhldGljcyB3aGVuIHdvcmtpbmcgd2l0aCBtdWx0aXBsZSBnZW9tczoqKiBBcyB5b3UnbGwgc2VlIGluIHRoZSBmb2xsb3dpbmcgY29kZSwgd2UndmUgbWFkZSBzb21lIGFkanVzdG1lbnRzIGR1ZSB0byB3b3JraW5nIHdpdGggbXVsdGlwbGUgKipnZW9tXF9cKioqIGxheWVycy4gU2luY2UgdGhlcmUgY2FuIGJlIG92ZXJsYXBwaW5nIGFlc3RoZXRpY3MgYmV0d2VlbiBnZW9tcywgeW91IG5lZWQgdG8gYmUgY29nbml6YW50IG9mIHRoZWlyIGVmZmVjdHMuIFJhdGhlciB0aGFuIHNldCB0aGVzZSBwYXJhbWV0ZXJzIGluIHRoZSAqKmFlcygpKiogbGF5ZXIsIHNldCB0aGVtIGRpcmVjdGx5IGluIHRoZWlyIHJlc3BlY3RpdmUgZ2VvbXMuDQo6OjoNCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBzY2F0dGVyLXN0eWxlIHVzaW5nIHRoZSByZW9yZGVyIGZ1bmN0aW9uDQpwaHVfaW5mb3JtYXRpb24uZGYgJT4lIA0KICAjIFJlbW92ZSB0aGUgdG9wIHJvdyAoT250YXJpbyBkYXRhKQ0KICBzbGljZSgyOm4oKSkgJT4lIA0KICAjIEFycmFuZ2Ugb3VyIGRhdGEgaGVyZSB0byBjbGVhbiB1cCB0aGUgY29kZQ0KICBhcnJhbmdlKGRlc2MocG9wdWxhdGlvbikpICU+JSANCiAgIyBVc2UgdGhlIHVwZGF0ZWQgb3JkZXIgdG8gcmVvcmRlciB0aGUgcHVibGljX2hlYWx0aF91bml0IHZhcmlhYmxlDQogIG11dGF0ZShwdWJsaWNfaGVhbHRoX3VuaXQgPSBmYWN0b3IocHVibGljX2hlYWx0aF91bml0LCBsZXZlbHMgPSAuJHB1YmxpY19oZWFsdGhfdW5pdCkpICU+JSANCiAgDQogIGdncGxvdCguKSArDQogICAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICAgIGFlcyh4ID0gcHVibGljX2hlYWx0aF91bml0LCB5ID0gZGVhdGhzX2NvdW50KSArDQogICAgICANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdCA9IDEsIHZqdXN0PTAuNSkpICsgIyBBZGp1c3Qgb3VyIHgtYXhpcyB0ZXh0DQogIA0KICAgICAgZ3VpZGVzKHNpemUgPSBndWlkZV9sZWdlbmQodGl0bGU9IlBvcHVsYXRpb25cbnNpemUiKSwgDQogICAgICAgICAgICAgY29sb3VyID0gIm5vbmUiKSArICMgU2V0IGNvbG9yIGxlZ2VuZCB0byBub25lIHRvIHJlbW92ZSBpdCANCiAgDQogICAgICAjIDMuIFNjYWxpbmcNCiAgICAgICMgc2V0IHRoZSByYW5nZSBmb3Igc2l6aW5nIG91ciBkb3RzDQogICAgICBzY2FsZV9zaXplKHJhbmdlID0gYygxLCAxMCksIG5hbWUgPSAiUG9wdWxhdGlvbiAoTSkiKSArIA0KICANCiAgICAgICMgNC4gR2VvbXMNCiAgICAgICMjIyAzLjIuMyBNYWtlIHRoZSBzdGljayBvZiB0aGUgbG9sbGlwb3ANCiAgICAgIC4uLihhZXMoeD1wdWJsaWNfaGVhbHRoX3VuaXQsIHhlbmQ9Li4uLCANCiAgICAgICAgICAgICAgICAgICAgICAgeT0uLi4sIHllbmQ9Li4uKSkgKw0KICANCiAgICAgICMjIyAzLjIuMyBQdXQgdGhlIGNhbmR5IG9uIHRvcA0KICAgICAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IHBvcHVsYXRpb24pLCBjb2xvdXIgPSAib3JjaGlkIikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMy4zLjAgQmFycGxvdHMgdG8gY29udmV5IHByb3BvcnRpb24gb3IgY29tcG9zaXRpb24NCg0KTGV0J3MgcmV2aXNpdCBvdXIgcHVibGljIGhlYWx0aCB1bml0IGRhdGEgZnJvbSBsYXN0IGxlY3R1cmUuIFdlJ2xsIHVzZSBhbiB1cGRhdGVkIGxvbmctZm9ybWF0IHZlcnNpb24gdGhhdCB3ZSBjcmVhdGVkIGFuZCBzYXZlZCBpbiBhIGNzdiBmb3JtYXQuIEFzIHlvdSBtaWdodCByZWNhbGwgdGhpcyBkYXRhc2V0IGNvbnRhaW5zIGEgY29sdW1uIG9mIGRhdGVzIHdpdGggZWFjaCByb3cgcmVwcmVzZW50aW5nIGFuIG9ic2VydmF0aW9uIG9mIGBuZXdfY2FzZXNgIHJlcG9ydGVkIGJ5IGEgYHB1YmxpY19oZWFsdGhfdW5pdGAgb24gdGhhdCBkYXRlLiBXZSBoYXZlIGEgNHRoIGNvbHVtbiBgdG90YWxfcGh1X25ld2AgcmVwcmVzZW50aW5nIHRvdGFsIGNhc2VzIHJlcG9ydGVkIGFjcm9zcyBhbGwgUEhVcyBvbiB0aGF0IHNwZWNpZmljIGRhdGUuDQoNCnwgICAgICAgICAgZGF0ZSAgICAgICAgICAgfCBwdWJsaWNfaGVhbHRoX3VuaXQgfCAgIG5ld19jYXNlcyAgICB8IHRvdGFsX3BodV9uZXcgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS06fDotLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLTp8DQp8IERhdGUgZm9ybWF0OiBZWVlZLU1NLUREIHwgZmFjdG9yIG9mIDM0IFBIVXMgIHwgbnVtZXJpYzpkb3VibGUgfCBudW1lcmljOmRvdWJsZSB8DQp8ICAgICAgICAgICAuLi4gICAgICAgICAgIHwgICAgICAgIC4uLiAgICAgICAgIHwgICAgICAuLi4gICAgICAgfCAgICAgIC4uLiAgICAgICB8DQoNCkluIG91ciBmaXJzdCBsZWN0dXJlIHdlIGxvb2tlZCBhdCB0aGUgb3RoZXIgaGVscGZ1bCB2aXN1YWxpemF0aW9uIHRoYXQgYmFycGxvdHMgY2FuIHByb2R1Y2U6IGNvbXBvc2l0aW9uIGFuZCBwcm9wb3J0aW9ucy4gSGVyZSB3ZSBjYW4gY29tYmluZSB0aGUgcHJvcG9ydGlvbnMgb2YgY2F0ZWdvcmllcyBvZiBkYXRhIHRvIGhlbHAgY29udmV5IGFuIGFkZGVkIGRpbWVuc2lvbiB0byBvdXIgZGF0YS4gQnkgc3RhY2tpbmcgUEhVcyBpbiBvdXIgIm5ldyBjYXNlIiBkYXRhIHdlIGFyZSBub3cgcGxvdHRpbmcgbmV3IGNhc2VzIHBlciBtb250aCBmb3IgbXVsdGlwbGUgUEhVcy4gVGhlIGFyZWEgb2YgZWFjaCBzdGFjaywgaG93ZXZlciwgYWxzbyBnaXZlcyB1cyBhIHNlbnNlIG9mIHByb3BvcnRpb25zIGZvciBlYWNoIFBIVSB0aGF0IHdlJ3ZlIGFkZGVkLg0KDQpgYGB7cn0NCiMgT3BlbiBvdXIgZGF0YSBmaWxlIGFuZCBkZWZpbmUgdGhlIGNvbHVtbiB0eXBlcyByYXRoZXIgdGhhbiBsZXQgdGhlIGZ1bmN0aW9uIGRlY2lkZS4NCmNvdmlkX3BodS5kZiA8LSByZWFkX3RzdiguLi4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcz0oLi4uKSkgIyBEZWZpbmUgY29sdW1uIHR5cGVzIGhlcmUNCg0KIyBUYWtlIGEgcGVlayBhdCB0aGUgZGF0YQ0KaGVhZChjb3ZpZF9waHUuZGYpDQoNCnRhaWwoY292aWRfcGh1LmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4zLjEgV29ya2luZyB3aXRoIERhdGVzIGFuZCB0aGUgYGx1YnJpZGF0ZWAgcGFja2FnZQ0KDQpMYXN0IHdlZWsgd2UgYnJlZXplZCBvdmVyIHRoZSBpZGVhIG9mIHdvcmtpbmcgd2l0aCB0aGUgZGF0ZSBmb3JtYXQgaW4gUi4gSXQgY2FuIGJlIGEgcmF0aGVyIGVzb3RlcmljIHN1YmplY3QgYnV0IGl0IGFsbCBjb21lcyBkb3duIHRvIGhvdyAqd2UgYXMgYSBzb2NpZXR5KiB0cmFjayBkYXRlcy4gRm9yIFIsIHRoZSBkYXRlIGlzIHN0b3JlZCBhcyBhbiBpbnRlZ2VyIHZhbHVlcyByZXByZXNlbnRpbmcgdGhlIG51bWJlciBvZiBkYXlzIHNpbmNlICoqMTk3MC0wMS0wMSoqLiBTbyB5ZXMsIHRoZXJlIG11c3QgZXhpc3Qgc29tZSAibmVnYXRpdmUiIGRhdGVzLg0KDQpUaGUgYGx1YnJpZGF0ZWAgcGFja2FnZSBpcyBoZXJlIHRvIGhlbHAgeW91IHNpbXBsaWZ5IHdvcmtpbmcgd2l0aCBkYXRlcyBhbmQgaGFzIGEgbnVtYmVyIG9mIGhlbHBmdWwgZnVuY3Rpb25zIHRoYXQgY2FuIGJlIHVzZWQgdG8gZXh0cmFjdCBpbmZvcm1hdGlvbiBmcm9tIHlvdXIgZGF0ZS4gRm9yIHVzLCB3ZSdsbCB1c2UgdGhlIGBmbG9vcl9kYXRlKClgIGZ1bmN0aW9uIHRvIGhlbHAgcm91bmQgb3VyIGRhdGVzIGRvd24gdG8gdGhlaXIgYFllYXItTW9udGhgIGZvcm1hdC4gVGhpcyBmdW5jdGlvbiB0YWtlcyBqdXN0IHR3byBwYXJhbWV0ZXJzOg0KDQotICAgYGRhdGVgOiB5b3VyIGRhdGUgb3IgdmVjdG9yIG9mIGRhdGVzIHRvIGNvbnZlcnQNCg0KLSAgIGB1bml0YDogdGhlIHVuaXQgYnkgd2hpY2ggeW91IHdhbnQgdG8gcm91bmQgZG93biAtIHRoaXMgY291bGQgYmUgeWVhciwgbW9udGgsIC4uLiwgc2Vjb25kIChpZiB5b3VyIGRhdGUgaXMgdHJhY2tpbmcgdGhhdCBzcGVjaWZpY2FsbHkpDQoNCk5vdGUgdGhhdCBvdXIgZGF0ZXMgd2lsbCAqc3RpbGwqIGJlIGluIGEgYGRhdGVgIG9iamVjdCBmb3JtYXQhDQoNCmBgYHtyfQ0KIyBXaGF0IGRvIG91ciBkYXRlcyBsb29rIGxpa2U/DQpoZWFkKGNvdmlkX3BodS5kZiRkYXRlKQ0KDQojIFdoYXQgZG8gdGhleSBsb29rIGxpa2UgYWZ0ZXIgcm91bmRpbmcgZG93biBieSBtb250aD8NCmZsb29yX2RhdGUoY292aWRfcGh1LmRmJGRhdGUsIHVuaXQgPSAuLi4pICU+JSBoZWFkKCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMy4yIFVzZSB0aGUgYHNjYWxlX3hfZGF0ZSgpYCBsYXllciB0byBzZXQgeW91ciB4LWF4aXMgaW5mb3JtYXRpb24NCg0KTGV0J3MgdXNlIHRoaXMgdG8gY29udmVydCBvdXIgZGF0ZSB2YWx1ZXMgZm9yIHBsb3R0aW5nIGFzIGJhciBncmFwaHMhIFdlJ2xsIGNvbnZlcnQgb3VyIHgtYXhpcyB2YWx1ZXMgaW4gb3VyIGBhZXMoKWAgY2FsbC4gT3VyIGRhdGEgb25seSBnb2VzIG91dCB0byBKYW51YXJ5IDI1LCAyMDIzIHNvIHdlJ2xsIHNldCBvdXIgbGltaXRzIHRvIGFjY29tbW9kYXRlIHRoYXQgcG9pbnQuDQoNCk5vdGUgYWxzbywgb3VyIGBzY2FsZV94X2RhdGUoKWAgbGF5ZXIuIFdlIHVzZWQgdGhpcyBsYXN0IHdlZWsgdG8gaGVscCBzZXQgaG93IG91ciB4LWF4aXMgaXMgZGlzcGxheWVkIHRoZSBkYXRhIGluY2x1ZGluZyBwYXJhbWV0ZXJzIGxpa2U6DQoNCi0gICBgbGltaXRzYDogd2hlcmUgb3VyIHgtYXhpcyBiZWdpbnMgYW5kIGVuZHMNCg0KLSAgIGBkYXRlX2JyZWFrc2A6ICpob3cqIHdlIHdvdWxkIGxpa2Ugb3VyIHgtYXhpcyB0aWNrcyB0byBicmVhayB1cA0KDQotICAgYGRhdGVfbGFiZWxzYDogKmhvdyogd2Ugd2FudCBvdXIgeC1heGlzIHRpY2tzIHRvIGJlIGxhYmVsZWQNCg0KLSAgIGBleHBhbmRgOiBkZXRlcm1pbmUgaWYgd2Ugd291bGQgbGlrZSBhZGRpdGlvbmFsIHBhZGRpbmcgb24gdGhlIHNpZGVzIG9mIG91ciB4LWF4aXMuIFRoaXMgaXMgYSA0LWVsZW1lbnQgdmVjdG9yIGJ1dCBjYW4gYWxzbyBhY2NlcHQgMiB2YWx1ZXMgYGMobXVsdCwgYWRkKWAgd2hlcmUgeW91IG1heSBlaXRoZXIgZXhwYW5kIG91dHdhcmRzIGJ5IG11bHRpcGx5aW5nIGxpbWl0cyBieSBgbXVsdGAgb3IgeW91IGNhbiBwYWQgdGhlIGxpbWl0cyB3aXRoIHRoZSB1bml0IHZhbHVlIChkYXlzIGluIHRoaXMgY2FzZSkgb2YgYGFkZGAuDQoNClRocm91Z2ggc29tZSBleHBlcmltZW50YXRpb24sIHdlJ2xsIHNldCBvdXIgbGltaXRzIHRvIFx+MS8yIG1vbnRoIGJlZm9yZSBvdXIgc3RhcnQgZGF0ZSBhbmQgZW5kIGRhdGVzLiBXZSdsbCBhaW0gdG8gZGlzcGxheSBvdXIgZGF0YSBmcm9tIERlY2VtYmVyIDIwMjIgdG8gSmFudWFyeSAyMDIzIGJ1dCByZW1lbWJlciB0aGF0IHRoaXMgZGF0YSBpcyAqKipmbG9vcmVkKioqIHRvIHRoZSBmaXJzdCBkYXRlIG9mIGVhY2ggbW9udGghDQoNCmBgYHtyLCBmaWcud2lkdGggPSAyMCwgZmlnLmhlaWdodCA9IDEwfQ0KDQojIFN0YXJ0IHdpdGggb3VyIFBIVSBjb3ZpZCBkYXRhDQpjb3ZpZF9waHUuZGYgJT4lIA0KICAjIEZpbHRlciB0byBqdXN0IGxvb2sgYXQgYSBmZXcgUEhVcw0KICBmaWx0ZXIocHVibGljX2hlYWx0aF91bml0ICVpbiUgYygiVG9yb250byIsICJQZWVsIiwgIllvcmsgUmVnaW9uIiwgIk90dGF3YSIpKSAlPiUgDQogIA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAgICMgMi4gQWVzdGhldGljcw0KICAgICAgYWVzKHggPSAuLi4sICMjIEZsb29yIHRoZSBkYXRlDQogICAgICAgICAgeT0gLi4uLCANCiAgICAgICAgICAjIHNldCBvdXIgZmlsbCBjb2xvdXIgaW5zdGVhZCBvZiBsaW5lIGNvbG91cg0KICAgICAgICAgIGZpbGwgPSBmY3RfcmVvcmRlcihwdWJsaWNfaGVhbHRoX3VuaXQsIG5ld19jYXNlcywgLmRlc2M9VFJVRSkpICsgDQogIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArICMgc2V0IHRleHQgc2l6ZQ0KICANCiAgICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlPSJQdWJsaWMgSGVhbHRoIFVuaXQiKSkgKw0KICAgICAgeGxhYigiRGF0ZSIpICsgIyBTZXQgdGhlIHgtYXhpcyBsYWJlbA0KICAgICAgeWxhYigiTmV3IGNhc2VzIikgKyAjIFNldCB0aGUgeS1heGlzIGxhYmVsDQogICAgICBnZ3RpdGxlKCJOZXcgY2FzZXMgcGVyIGRheSBhY3Jvc3MgYWxsIE9udGFyaW8gUHVibGljIEhlYWx0aCBVbml0cyIpICsNCiAgDQogICAgICAjIDMuIFNjYWxpbmcNCiAgICAgICMjIyAzLjMuMiBTY2FsZSBvdXIgZGF0ZXMgZnJvbSBtaWQtTm92ZW1iZXIgdG8gTWlkLUphbnVhcnkuDQogICAgICBzY2FsZV94X2RhdGUobGltaXRzID0gYyhhcy5EYXRlKCIyMDIxLTExLTE1IiksIGFzLkRhdGUoIjIwMjMtMDEtMTUiKSksIA0KICAgICAgICAgICAgICAgICAgIGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYi0lWSIsIA0KICAgICAgICAgICAgICAgICAgIGV4cGFuZCA9IGMoMCw1KSkgKyAjIFBhZCB0aGUgeC1heGlzIGJ5IDUgZGF5cw0KICAgICAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKSArICMgdGhlICJkIiBzdGFuZHMgZm9yIGRpc2NyZXRlIGNvbG91ciBzY2FsZQ0KICANCiAgICAgICMgNC4gR2VvbXMNCiAgICAgIGdlb21fY29sKCkgIyBTZXQgdXAgb3VyIGJhcnBsb3QgaGVyZQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAzLjQuMCBDb252ZXJ0IHlvdXIgcGxvdHMgdG8gYSBjaXJjdWxhciBsYXlvdXQgd2l0aCBgY29vcmRfcG9sYXIoKWANCg0KR2VuZXJhbGx5IHNwZWFraW5nIHRoZXJlIGFyZSBhIG51bWJlciBvZiBjaXJjdWxhciBvciBwb2xhciBjb29yZGluYXRlIHBsb3QgdmFyaWFudHMgcmFuZ2luZyBpbiBjb21wbGV4aXR5IGZyb20gdGhlIHBpZSBjaGFydCB0byBhIHJhY2V0cmFjayBjaGFydC4gVGhlIGBjb29yZF9wb2xhcigpYCBsYXllciB0YWtlcyBhIGZldyBwYXJhbWV0ZXJzOg0KDQotICAgYHRoZXRhYDogZGV0ZXJtaW5lcyBpZiBhbmdsZXMgd2lsbCBiZSBtYXBwZWQgdG8gdGhlIHggb3IgeSB2YXJpYWJsZQ0KLSAgIGBzdGFydGA6IG9mZnNldCBmcm9tIHRoZSAxMiBvJ2Nsb2NrIHBvc2l0aW9uDQotICAgYGRpcmVjdGlvbmA6IDEgPSBjbG9ja3dpc2UsIC0xID0gY291bnRlci1jbG9ja3dpc2UNCg0KSGVyZSdzIGEgc3VtbWFyeQ0KDQp8IENoYXJ0IG5hbWUgICAgICAgICAgICAgICB8IFBsb3QgZGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgVXNlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IENhcnRlc2lhbiBhbmFsb2cgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLXwNCnwgUGllIGNoYXJ0ICAgICAgICAgICAgICAgIHwgU2xpY2VkIHVwIHByb3BvcnRpb25zIG9mIGEgY2lyY2xlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBTaW1wbGUgcHJvcG9ydGlvbiBjb21wYXJpc29uIGJldHdlZW4gZ3JvdXBzICAgICAgIHwgc2luZ2xlIGJhciB2YXJpYW50ICAgICAgICAgICB8DQp8IE5pZ2h0aW5nYWxlL0NveGNvbWIgcGxvdCB8IEludGVydmFscyBhcmUgZXF1YWwgd2VkZ2VzIGJ1dCBzaGFkZWQgYXJlYXMgcmVwcmVzZW50IHByb3BvcnRpb25zIHwgSW50ZXJ2YWxzIHdpdGggYWRkaXRpb25hbCBjYXRlZ29yaWNhbCBwcm9wb3J0aW9ucyB8IFN0YWNrZWQgY29sdW1uIHBsb3QsIHRoZXRhPXggfA0KfCBSYWNlIHRyYWNrIHBsb3QgICAgICAgICAgfCBJbnRlcnZhbHMgYXJlIHNwbGl0IGNvbmNlbnRyaWNhbGx5IHRvIGZvcm0gcmluZ3Mgb2YgZXF1YWwgd2lkdGggICB8IEhlbHBmdWwgaWYgc29tZSBncm91cHMgYXJlIGxlc3MgZGl2ZXJzZSAgICAgICAgICAgfCBTdGFjayBjb2x1bW4gcGxvdCwgdGhldGE9eSAgIHwNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAzLjQuMSBQaWUgY2hhcnRzIHdvcmsgYmVzdCB3aXRoIHNpbXBsZSBkYXRhDQoNCkZyb20gb3VyIHBpZSBjaGFydCBkZXNjcmlwdGlvbiBhbmQgZnJvbSB5b3VyIG93biBleHBlcmllbmNlLCB5b3UgY2FuIHJlY2FsbCB0aGF0IHBpZSBjaGFydHMgYXJlbid0IGhlbHBmdWwgd2l0aCBjb21wbGV4IGRhdGEuIE9uY2UgdGhlIG51bWJlciBvZiBjYXRlZ29yaWVzIHN1cnBhc3NlcyA2LTggZ3JvdXBzLCB0aGluZ3MgY2FuIGdldCBoYXJkIHRvIHZpc3VhbGx5IGRpZ2VzdCAtIGVzcGVjaWFsbHkgaWYgb25lIGNhdGVnb3J5IGRvbWluYXRlcyB0aGUgb3RoZXJzLg0KDQpMZXQncyB1c2UgYHBodV9pbmZvcm1hdGlvbi5kZmAgdG8gYXNzZXNzIHRoZSBjdW11bGF0aXZlIENPVklELTE5IGNhc2UgZGF0YSBmcm9tIGFjcm9zcyBvdXIgUEhVcyB0byBsb29rIGF0IHRoZSB0b3AgNCBQSFVzIGJ5IGNhc2Vsb2FkLiBXZSdsbCBjb252ZXJ0IGEgYGdlb21fYmFyKClgIGJhcnBsb3QgdG8gb3VyIHBpZSBjaGFydCB1c2luZyB0aGUgYGNvb3JkX3BvbGFyKClgIGxheWVyLg0KDQpUbyBhY2NvbXBsaXNoIHRoaXMgZmVhdCBpdCBoZWxwcyB0byB0aGluayBhYm91dCBob3cgdGhlIGRhdGEgaXMgYmVpbmcgaGFuZGxlZC4gSWYgeW91IHRoaW5rIGFib3V0IGEgcGllIGNoYXJ0LCB5b3UgY2FuIGltYWdpbmUgaXQgbXVjaCBsaWtlIGEgc3RhY2tlZCBiYXJwbG90IHdoZXJlIHRoZSB0b3AgYW5kIGJvdHRvbSBoYXZlIGJlZW4gY3VydmVkIGFyb3VuZCB0byBtZWV0IGVhY2ggb3RoZXIuIFRoaXMgaXMgYSBwbG90IGFsbCBhYm91dCAqcHJvcG9ydGlvbnMqIGFuZCBpbnN0ZWFkIG9mIGhhdmluZyBhbiB4LWF4aXMgZGVmaW5lZCwgaXQgaXMgdGhlIHByb3BvcnRpb25zIGZvciBhIHNpbmdsZSBjYXRlZ29yeSBvciBncm91cC4NCg0KVGhlcmVmb3JlLCB3ZSBzZXQgdGhlIGBhZXMoKWAgcGFyYW1ldGVyIGB4YCB0byBhIHZhbHVlIG9mIGAiImAgc28gdGhlcmUgYXJlIG5vIGNhdGVnb3JpY2FsIHZhbHVlcyBmb3IgYHhgIGJ1dCB5b3Ugd2lsbCBzdGlsbCBuZWVkIHRvIHNldCBhIGB5YCB2YWx1ZSBmcm9tIHdoaWNoIHRvIGRldGVybWluZSB0aG9zZSBwcm9wb3J0aW9ucy4NCg0KYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTB9DQoNCiMgR28gdG8gdGhlIHNpbXBsZSBwaHUgaW5mb3JtYXRpb24gdGFibGUNCnBodV9pbmZvcm1hdGlvbi5kZiAlPiUgDQogICMgU29ydCBmb3Igb25seSBhIGZldyBQSFVzDQogIGZpbHRlcihwdWJsaWNfaGVhbHRoX3VuaXQgJWluJSBjKCJUb3JvbnRvIiwgIlBlZWwiLCAiWW9yayBSZWdpb24iLCAiT3R0YXdhIikpICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgICMjIyAzLjQuMSBTZXQgdGhlIHggYXhpcyBjb3JyZWN0bHkNCiAgICBhZXMoeD0uLi4sIHk9Li4uLCANCiAgICAgICAgZmlsbCA9IC4uLikgKyAjIHNldCBvdXIgZmlsbCBjb2xvdXIgaW5zdGVhZCBvZiBsaW5lIGNvbG91cg0KDQogICAgdGhlbWVfdm9pZCgpICsgIyBUaGlzIHdpbGwgcmVtb3ZlIGFsbCBiYWNrZ3JvdW5kIHRoZW1lIG9iamVjdHMNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpICsgIyBzZXQgdGV4dCBzaXplDQoNCiAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUHVibGljIEhlYWx0aCBVbml0IikpICsNCiAgICB4bGFiKCIiKSArICMgU2V0IHRoZSB4LWF4aXMgbGFiZWwNCiAgICB5bGFiKCJOZXcgY2FzZXMiKSArICMgU2V0IHRoZSB5LWF4aXMgbGFiZWwNCiAgICBnZ3RpdGxlKCJUb3RhbCBjYXNlcyBieSBoZWFsdGggdW5pdCIpICsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkgKyAjIHRoZSAiZCIgc3RhbmRzIGZvciBkaXNjcmV0ZSBjb2xvdXIgc2NhbGUNCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2Jhcih3aWR0aD0uLi4sIHN0YXQ9Li4uLCBjb2xvdXIgPSAuLi4pICsgIyBTZXQgdXAgb3VyIGJhcnBsb3QgaGVyZQ0KDQogICAgIyA3IENvb3JkaW5hdGUgc3lzdGVtDQogICAgIyMjIDMuNC4xIE1hcCBhbmdsZXMgdG8gdGhlIFktYXhpcw0KICAgIC4uLg0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy40LjIgTmlnaHRpbmdhbGUgcm9zZSBjaGFydHMgZW1waGFzaXplIHByb3BvcnRpb25zDQoNCk1hZGUgZmFtb3VzIGJ5IFtGbG9yZW5jZSBOaWdodGluZ2FsZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRmxvcmVuY2VfTmlnaHRpbmdhbGUpLCB0aGVzZSBwbG90cyAod2hpY2ggc2hlIG5hbWVkIFtjb3hjb21iIHBsb3RzXShodHRwczovL2RhdGF2aXpjYXRhbG9ndWUuY29tL21ldGhvZHMvbmlnaHRpbmdhbGVfcm9zZV9jaGFydC5odG1sKSkgd2VyZSB1c2VkIHRvIGhlbHAgZW1waGFzaXplIHRoZSBwcm9wb3J0aW9ucyBvZiBzb2xkaWVyIGRlYXRocyBkdWUgdG8gaW5mZWN0aW9uIGR1cmluZyB0aGUgQ3JpbWVhbiB3YXIuIEVhY2ggY2lyY3VsYXIgaW5jcmVtZW50IGlzIGVxdWFsbHkgc3BhY2VkIHRvIHJlcHJlc2VudCBpbmNyZWFzaW5nIHZhbHVlcyBhbG9uZyB0aGUgeS1heGlzLiBWaXN1YWxseSwgdGhpcyBnaXZlcyBtb3JlIGFyZWEgcmVwcmVzZW50YXRpb24gYXMgd2UgcmFkaWF0ZSBvdXR3YXJkcyBvbiB0aGUgY2hhcnQuIFRoaXMgcHJvZHVjZXMgYSBjaGFydCB0aGF0DQoNCjEuICBNYWtlcyBkaXN0aW5ndWlzaGluZyBsYXJnZXIgcHJvcG9ydGlvbnMgbW9yZSBvYnZpb3VzLg0KMi4gIEFkZHMgYSBuZXcgZGltZW5zaW9uIHRvIHRoZSBwaWUgY2hhcnQsIGFsbG93aW5nIHVzIHRvIGNyZWF0ZSBpbnRlcnZhbHMgZm9yIGNvbXBhcmlzb24uDQozLiAgSGVscHMgY29ubmVjdCBvdXIgc3RhcnQgYW5kIGVuZCBjYXRlZ29yeSwgZXNwZWNpYWxseSBpZiB0aGV5IGRvbid0IHJlcHJlc2VudCBhbiBvcmRpbmFsIG9yIGNvbnRpbnVvdXMgc2NhbGUuDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovTmlnaHRpbmdhbGUtbW9ydGFsaXR5LmpwZz9yYXc9dHJ1ZSIgd2lkdGg9IjEwMDAiLz4NCg0KRmxvcmVuY2UgTmlnaHRpbmdhbGUncyBvcmlnaW5hbCByb3NlIGNoYXJ0L2NveGNvbWIgcGxvdCBwdWJsaWNhdGlvbi4gU291cmNlZCBmcm9tIHRoZSBbV2lraW1lZGlhIGNvbW1vbnNdKGh0dHBzOi8vY29tbW9ucy53aWtpbWVkaWEub3JnL3dpa2kvRmlsZTpOaWdodGluZ2FsZS1tb3J0YWxpdHkuanBnKQ0KOjo6DQoNCioqQ2F1dGlvbioqOiBvdXRlciBzZWdtZW50cyBkaXNwcm9wb3J0aW9uYXRlbHkgcmVwcmVzZW50IGluY3JlYXNlcyBpbiB2YWx1ZS4gVGhlaXIgbGFyZ2VyIGFyZWEgcHJvZHVjZXMgbW9yZSB2aXN1YWwgZW1waGFzaXMgZGVzcGl0ZSB0aGUgbGluZWFyIHNjYWxlIG9mIHRoZSB5LWF4aXMuDQoNCldlJ2xsIHN3aXRjaCBiYWNrIHRvIG91ciBQSFUgZGFpbHkgY2FzZSBkYXRhIGZyb20gYGNvdmlkX3BodS5kZmAgdG8gZ2VuZXJhdGUgc29tZSBzdGFja2FibGUgaW5mb3JtYXRpb24uDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCioqS25vdyB5b3VyIHktbGltaXRzISoqIE5vdGUgdGhhdCBkdWUgdG8gdGhlIGRpc3Byb3BvcnRpb25hdGUgYXJlYSBpbmNyZWFzZXMgd2l0aCBpbmNyZWFzaW5nIHktYXhpcyB2YWx1ZXMsIHlvdSBuZWVkIHRvIGNvbnNpZGVyIHRoZSAqcmFuZ2UqIG9mIHlvdXIgZGF0YS4gV2UndmUgc2VlbiB0aGUgaGlnaC12YWx1ZXMgaW4gb3VyIEphbnVhcnkgMjAyMiBkYXRhIHNvIHdlJ2xsIHNldCB0aGUgeC1heGlzIHNjYWxlIGFnYWluIHdpdGggKipzY2FsZV94X2RhdGUoKSoqIHRvIHJhbmdlIGZyb20gRmVicnVhcnkgMjAyMiB0byBKYW51YXJ5IDIwMjMuDQo6OjoNCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0NCg0KIyBTdGFydCB3aXRoIG91ciBQSFUgY292aWQgZGF0YQ0KY292aWRfcGh1LmRmICU+JSANCiAgIyBGaWx0ZXIgdG8ganVzdCBsb29rIGF0IGEgZmV3IFBIVXMNCiAgZmlsdGVyKHB1YmxpY19oZWFsdGhfdW5pdCAlaW4lIGMoIlRvcm9udG8iLCAiUGVlbCIsICJZb3JrIFJlZ2lvbiIsICJPdHRhd2EiKSkgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHggPSBmbG9vcl9kYXRlKGRhdGUsIHVuaXQgPSAibW9udGgiKSwgeT0gbmV3X2Nhc2VzLCANCiAgICAgICAgIyBzZXQgb3VyIGZpbGwgY29sb3VyIGluc3RlYWQgb2YgbGluZSBjb2xvdXINCiAgICAgICAgZmlsbCA9IGZjdF9yZW9yZGVyKHB1YmxpY19oZWFsdGhfdW5pdCwgbmV3X2Nhc2VzLCAuZGVzYz1UUlVFKSkgKyANCg0KICAgIHRoZW1lX2J3KCkgKyAjIG1ha2UgdGhlIHRoZW1lIG1vcmUgcGxhaW4NCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpICsgIyBzZXQgdGV4dCBzaXplDQogICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKGNvbG9yPSJncmV5NjAiKSkgKyAjIGRhcmtlbiBvdXIgbWFqb3IgeSBncmlkDQoNCiAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUHVibGljIEhlYWx0aCBVbml0IikpICsNCiAgICB4bGFiKCJEYXRlIikgKyAjIFNldCB0aGUgeC1heGlzIGxhYmVsDQogICAgeWxhYigiTmV3IGNhc2VzIikgKyAjIFNldCB0aGUgeS1heGlzIGxhYmVsDQogICAgZ2d0aXRsZSgiTmV3IGNhc2VzIHBlciBkYXkgYWNyb3NzIGFsbCBPbnRhcmlvIFB1YmxpYyBIZWFsdGggVW5pdHMiKSArDQoNCiAgICAjIDMuIFNjYWxpbmcNCiAgICBzY2FsZV94X2RhdGUobGltaXRzID0gYyhhcy5EYXRlKCIyMDIyLTAxLTE1IiksIGFzLkRhdGUoIjIwMjMtMDEtMTUiKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTY2FsZSB0aGUgZGF0ZXMgY29ycmVjdGx5IHRvIGdldCBhIG1vbnRoLVllYXIgZm9ybWF0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZV9icmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViLSVZIiwgZXhwYW5kID0gYygwLDApKSArIA0KICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkgKyAjIHRoZSAiZCIgc3RhbmRzIGZvciBkaXNjcmV0ZSBjb2xvdXIgc2NhbGUNCiAgICANCiAgICAjIDQuIEdlb21zDQogICAgIyMjIDMuNC4yIFNldCB1cCBvdXIgYmFycGxvdCBoZXJlDQogICAgLi4uICsgDQoNCiAgICAjIDcgQ29vcmRpbmF0ZSBzeXN0ZW0NCiAgICAjIyMgMy40LjIgQ29udmVydCB0aGUgYmFycGxvdCB0byBhIHBvbGFyIGNvb3JkaW5hdGUNCiAgICAuLi4NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuNC4zIFJhY2V0cmFjayBwbG90cyBhcmUgYW4gYWVzdGhldGljIHZhcmlhbnQgb2YgdGhlIGJhciBjaGFydA0KDQpZb3Ugd2FudCB0byBtYWtlIGFub3RoZXIgY29vbCBjaGFydCBmcm9tIGEgYm9yaW5nIGJhciBjaGFydC4gU3RhY2tlZCBvciBvdGhlcndpc2UsIHlvdSBjYW4gY29udmVydCB0aG9zZSBiYXIgcGxvdHMgdG8gYSByYWNldHJhY2sgb3IgcmFkaWFsIGJhciBjaGFydC4gVGhlIG9ubHkgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBjb3hjb21iIHBsb3QgYW5kIHJhZGlhbCBiYXIgY2hhcnQgaXMgdGhhdCBpbnN0ZWFkIG9mIHRoZSBkZWZhdWx0IGB0aGV0YT0ieCJgIHdlIHNldCBpdCB0byBgeWAuIExpa2UgZmxpcHBpbmcgdGhlIGNvb3JkaW5hdGVzIGZvciBhIGJhciBjaGFydC4NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC13YXJuaW5nfQ0KKipCZXdhcmUgdGhlIHJhY2V0cmFjayB0cmFuc2Zvcm1hdGlvbjoqKiBUaGlzIGlzIGFuIGFlc3RoZXRpYyB0cmFuc2Zvcm1hdGlvbiB3aGVyZSBlYWNoIGJhciBnZXRzIHJlbGF0aXZlbHkgbG9uZ2VyIGFzIHlvdSBtb3ZlIGZyb20gaW5zaWRlIHRvIG91dC4gVGhlcmVmb3JlLCB2YWx1ZXMgbXVzdCBiZSBqdWRnZWQgYnkgcmFkaWFucyEgT3VyIGV5ZXMgY2FuIG1vcmUgcHJlY2lzZWx5IGp1ZGdlIGRpZmZlcmVuY2VzIG9uIGEgQ2FydGVzaWFuIGJhciBjaGFydC4gVGh1cyB3aGVuIHZpZXdpbmcgdGhlc2UgdHlwZXMgb2YgY2hhcnRzLCBkb24ndCBiZSBtaXNsZWQgYnkgaG93IHRoZXkgbG9vayBhdCBhIGNhc3VhbCBnbGFuY2UhDQo6OjoNCg0KTGV0J3MgbWFrZSBhIHNldCBvZiBiYXJjaGFydCBkYXRhIGFuZCBjb21wYXJlIGl0IHRvIHRoZSByYWNldHJhY2sgcGxvdC4gTm90ZSB0aGF0IG5lZ2F0aXZlIHZhbHVlcyBpbiBvdXIgZGF0YXNldCBhcmUgcGxvdHRlZCBzZXBhcmF0ZWx5LiBJdCBpcyBhbiBpbXBsZW1lbnRhdGlvbiBxdWlyayBvZiBgZ2dwbG90YC4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBTdGFydCB3aXRoIG91ciBQSFUgY292aWQgZGF0YQ0KY292aWRfcGh1LmRmICU+JSANCiAgIyBGaWx0ZXIgdG8ganVzdCBsb29rIGF0IGEgZmV3IFBIVXMNCiAgZmlsdGVyKHB1YmxpY19oZWFsdGhfdW5pdCAlaW4lIGMoIlRvcm9udG8iLCAiUGVlbCIsICJZb3JrIFJlZ2lvbiIsICJPdHRhd2EiKSkgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHggPSBmbG9vcl9kYXRlKGRhdGUsIHVuaXQgPSAibW9udGgiKSwgeT0gbmV3X2Nhc2VzLCANCiAgICAgICAgIyBzZXQgb3VyIGZpbGwgY29sb3VyIGluc3RlYWQgb2YgbGluZSBjb2xvdXINCiAgICAgICAgZmlsbCA9IGZjdF9yZW9yZGVyKHB1YmxpY19oZWFsdGhfdW5pdCwgbmV3X2Nhc2VzLCAuZGVzYz1UUlVFKSkgKyANCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCg0KICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlPSJQdWJsaWMgSGVhbHRoIFVuaXQiKSkgKw0KICAgIHhsYWIoIkRhdGUiKSArICMgU2V0IHRoZSB4LWF4aXMgbGFiZWwNCiAgICB5bGFiKCJOZXcgY2FzZXMiKSArICMgU2V0IHRoZSB5LWF4aXMgbGFiZWwNCiAgICBnZ3RpdGxlKCJOZXcgY2FzZXMgcGVyIGRheSBhY3Jvc3MgYWxsIE9udGFyaW8gUHVibGljIEhlYWx0aCBVbml0cyIpICsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX3hfZGF0ZShsaW1pdHMgPSBjKGFzLkRhdGUoIjIwMjItMDEtMTUiKSwgYXMuRGF0ZSgiMjAyMy0wMS0xNSIpKSwNCiAgICAgICAgICAgICAgICAgZGF0ZV9icmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViLSVZIiwgZXhwYW5kID0gYygwLDUpKSArDQogICAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKSArICMgdGhlICJkIiBzdGFuZHMgZm9yIGRpc2NyZXRlIGNvbG91ciBzY2FsZQ0KICAgIA0KICAgICMgMy4gU2NhbGluZw0KICAgIC4uLiArICMjIyAzLjQuMyBGbGlwIHRoZSB4IGFuZCB5IGF4ZXMNCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2NvbCgpICMgU2V0IHVwIG91ciBiYXJwbG90IGhlcmUNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQ0KDQojIFN0YXJ0IHdpdGggb3VyIFBIVSBjb3ZpZCBkYXRhDQpjb3ZpZF9waHUuZGYgJT4lIA0KICAjIEZpbHRlciB0byBqdXN0IGxvb2sgYXQgYSBmZXcgUEhVcw0KICBmaWx0ZXIocHVibGljX2hlYWx0aF91bml0ICVpbiUgYygiVG9yb250byIsICJQZWVsIiwgIllvcmsgUmVnaW9uIiwgIk90dGF3YSIpKSAlPiUgDQogIA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeCA9IGZsb29yX2RhdGUoZGF0ZSwgdW5pdCA9ICJtb250aCIpLCB5PSBuZXdfY2FzZXMsIA0KICAgICAgICAjIHNldCBvdXIgZmlsbCBjb2xvdXIgaW5zdGVhZCBvZiBsaW5lIGNvbG91cg0KICAgICAgICBmaWxsID0gZmN0X3Jlb3JkZXIocHVibGljX2hlYWx0aF91bml0LCBuZXdfY2FzZXMsIC5kZXNjPVRSVUUpKSArIA0KDQogICAgdGhlbWVfYncoKSArICMgbWFrZSB0aGUgdGhlbWUgbW9yZSBwbGFpbg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCiAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3I9ImdyZXk2MCIpKSArICMgZGFya2VuIG91ciBtYWpvciB5IGdyaWQNCg0KICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlPSJQdWJsaWMgSGVhbHRoIFVuaXQiKSkgKw0KICAgIHhsYWIoIkRhdGUiKSArICMgU2V0IHRoZSB4LWF4aXMgbGFiZWwNCiAgICB5bGFiKCJOZXcgY2FzZXMiKSArICMgU2V0IHRoZSB5LWF4aXMgbGFiZWwNCiAgICBnZ3RpdGxlKCJOZXcgY2FzZXMgcGVyIGRheSBhY3Jvc3MgYWxsIE9udGFyaW8gUHVibGljIEhlYWx0aCBVbml0cyIpICsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX3hfZGF0ZShsaW1pdHMgPSBjKGFzLkRhdGUoIjIwMjItMDEtMTUiKSwgYXMuRGF0ZSgiMjAyMy0wMS0xNSIpKSwNCiAgICAgICAgICAgICAgICAgIyBTY2FsZSB0aGUgZGF0ZXMgY29ycmVjdGx5IHRvIGdldCBhIG1vbnRoLVllYXIgZm9ybWF0DQogICAgICAgICAgICAgICAgIGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYi0lWSIsIGV4cGFuZCA9IGMoMCw1KSkgKyANCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpICsgIyB0aGUgImQiIHN0YW5kcyBmb3IgZGlzY3JldGUgY29sb3VyIHNjYWxlDQogICAgDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fY29sKCkgKyAjIFNldCB1cCBvdXIgYmFycGxvdCBoZXJlDQoNCiAgICAjIDcgQ29vcmRpbmF0ZSBzeXN0ZW0NCiAgICAjIyMgMy40LjMgTm8gbmVlZCB0byBmbGlwIHRoZSBjb29yZGluYXRlcywganVzdCBzZXQgdGhldGE9eQ0KICAgIGNvb3JkX3BvbGFyKC4uLikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuNC40IEJhcnBsb3RzIGFuZCB0aGVpciB2YXJpYW50cyBzdW1tYXJpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkYXRhIGJldHdlZW4gZ3JvdXBzIG9yIGludGVydmFscyBidXQgTk9UIHdpdGhpbiBzaW5nbGUgcG9wdWxhdGlvbnMuDQoNCk9mdGVuIGZvciBvdXIgcHVycG9zZXMgYXMgc2NpZW50aXN0cyB3ZSBhcmUgbW9yZSBjb25jZXJuZWQgd2l0aCB0aGUgZGlzdHJpYnV0aW9uIG9mIHJlcGxpY2F0ZXMgb3IgbWVhc3VyZW1lbnRzIGFzIGEgcG9wdWxhdGlvbiAqd2l0aGluKiBhIGdyb3VwIHdoaWxzdCBhbHNvIGNvbXBhcmluZyBhY3Jvc3Mgb3RoZXIgZ3JvdXBzIChpZSBjb250cm9sIHZzIHRyZWF0bWVudCkuIE5haXZlbHksIHdlIGFyZSB0ZW1wdGVkIGJ5IHRoZSBjb252ZW5pZW5jZSBvZiBFeGNlbCB0byBzaW1wbGlmeSBvdXIgZGF0YSBhcyBhIGJhciBjaGFydCB3aXRoIHNvbWUgc2ltcGxlIHN0YW5kYXJkIGRldmlhdGlvbiBvciBlcnJvciBiYXJzLiBPbmUgd29yZCBjb25jaXNlbHkgZGVzY3JpYmVzIHRoaXMgYmVoYXZpb3VyOg0KDQojIyMgJCRcdGV4dHtJbmFwcHJvcHJpYXRlfSQkDQoNCk5vdGUgZnJvbSBvdXIgZXhhbXBsZXMgdGhhdCBhbHRob3VnaCB3ZSBhcmUgY29tcGFyaW5nIHB1YmxpYyBoZWFsdGggdW5pdHMsIG91ciBkYXRhcG9pbnRzIHJlcHJlc2VudCBzaW5nbGUgb3IgdG90YWwgb2JzZXJ2YXRpb25zIHNlcGFyYXRlZCBlaXRoZXIgYnkgZ3JvdXAgb3IgYnkgdGltZS4gQXQgZWFjaCB4LWF4aXMgcG9pbnQgd2UgYXJlIG5vdCBjb21wYXJpbmcgdGhlIGRpc3RyaWJ1dGlvbiBiZXR3ZWVuIGNhdGVnb3JpZXMgaW4gYSBtZWFuaW5nZnVsIHdheS4gSW4gZmFjdCwgZnJvbSBvdXIgZmlyc3QgZXhhbXBsZSwgd2UgY2FuIGNvbnZleSBhICpuZWFyLWV4YWN0KiBtZXNzYWdlIHVzaW5nIGEgZG90LXBsb3QhIEluIGVzc2VuY2UgdGhlc2UgdmlzdWFsaXphdGlvbnMgYXJlIGNvbW11bmljYXRpbmcgdGhlIGNhdGVnb3JpemF0aW9uIG9mIHNhbXBsZXMgYWNyb3NzIG11bHRpcGxlIGdyb3Vwcy4gTW9yZSBvciBsZXNzLCB0aGlzIGlzIGFib3V0IHZpc3VhbGl6aW5nICpwcm9wb3J0aW9ucyouDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtZGFuZ2VyfQ0KKipTZWN0aW9uIDMuMC4wIENvbXByZWhlbnNpb24gUXVlc3Rpb246KiogQWZ0ZXIgZXhwbG9yaW5nIHRoZSByYWNldHJhY2sgdmFyaWFudCBhYm92ZSwgdmlzdWFsaXppbmcgb3VyIGRhdGEgcmFuZ2luZyBmcm9tIEZlYnJ1YXJ5IDIwMjIgdG8gSmFudWFyeSAyMDIzLCB3aGF0IGlzIHRoZSBiaWdnZXN0IGlzc3VlIHlvdSBtaWdodCBzZWU/IFdoYXQgd291bGQgYmUgb25lIHdheSB0byByZW1lZHkgdGhpcz8gSXMgdGhpcyBhbiBhcHByb3ByaWF0ZSB2aXN1YWxpemF0aW9uIG9mIG91ciBkYXRhPw0KOjo6DQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9DQojIENvZGUgeW91ciBwbG90IGhlcmUhDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgNC4wLjAgRGVuc2l0eSBwbG90cyBjb21wdXRlIGEgdGhlb3JldGljYWwgZGlzdHJpYnV0aW9uDQoNCldoYXQgaXMgdGhlIHNoYXBlIG9mIG91ciBkYXRhPyBXaGVuIHdlIGVuY291bnRlciBleHBlcmltZW50YWwgcG9wdWxhdGlvbnMgYW5kIGJlZ2luIHNhbXBsaW5nIG9yIG1lYXN1cmUgZm9yIHNwZWNpZmljIGNoYXJhY3RlcmlzdGljcywgd2UgaW5ldml0YWJseSBiZWdpbiB0byByZXZlYWwgd2hldGhlciBvciBub3QgdGhhdCBjaGFyYWN0ZXJpc3RpYyBoYXMgYSBwcmVkaWN0YWJsZSByYW5nZSwgbWVkaWFuIHZhbHVlLCBldGMuIERlbnNpdHkgcGxvdHMsIG9uY2UgZ2l2ZW4gZW5vdWdoIHNhbXBsZSB2YWx1ZXMsIGJlZ2luIHRvIHByZWRpY3QgdGhlIHNoYXBlIG9yIGRpc3RyaWJ1dGlvbiBvZiB0aGF0IHNhbXBsaW5nLiBXZSBzb21ldGltZXMgdXNlIHRoaXMgdG8gcmVwcmVzZW50IHRoZSB0aGVvcmV0aWNhbCBkaXN0cmlidXRpb24gb2Ygb3VyIG9yaWdpbmFsIHBvcHVsYXRpb25zLg0KDQojIyA0LjAuMSBEaXN0cmlidXRpb24gcGxvdHMgbmVlZCBhcHByb3ByaWF0ZSBkYXRhDQoNCkluIGNvbnRyYXN0IHRvIG91ciBwcmV2aW91cyBkYXRhc2V0cywgd2UgYXJlIGludGVyZXN0ZWQgaW4gZGlzc2VjdGluZyBncm91cCBjaGFyYWN0ZXJzdGljcyBhZnRlciBzYW1wbGluZyBtdWx0aXBsZSB0aW1lcy4gVGhlcmVmb3JlLCBiZWZvcmUgY29udGludWluZyBvbiBvdXIgam91cm5leSwgd2UgbmVlZCB0byBmaW5kIHNvbWUgZGF0YSB0aGF0IGlzIGJldHRlciBhbmFseXNlZCBhcyBwb3B1bGF0aW9uIGRhdGEuIExldCdzIG9wZW4gdXAgc29tZSBtb3JlIFNBUlMtQ29WLTIgZGF0YSBmcm9tIE9udGFyaW8gUEhVcyB0aGF0IGhhcyBiZWVuIGJyb2tlbiBpbnRvIGFnZSBhbmQgc2V4IGRlbW9ncmFwaGljczogYE9udGFyaW9fYWdlX3NleF9DT1ZJRC0xOV9jYXNlcy5jc3ZgLiBXZSdsbCBzZWUgd2hhdCB3ZSBjYW4gZ2xlYW4gZnJvbSB0aGUgZGF0YS4NCg0KQmVmb3JlIHdlIGNhbiBkbyBhbnkgdmlzdWFsaXphdGlvbiB3ZSdsbCB3YW50IHRvIHRpZHkgdXAgdGhlIGRhdGEgaW4gdGhlIGZvbGxvd2luZyBzdGVwczoNCg0KMS4gIEltcG9ydCB0aGUgZGF0YSB3aXRoIGByZWFkX2NzdigpYA0KMi4gIEZpeCB0aGUgY29sdW1uIG5hbWVzIHRvIHJlbW92ZSBzcGFjZXMgYW5kIHJlcGxhY2UgdGhlbSB3aXRoICJcXyINCjMuICBQYXJlIGRvd24gb24gdGhlIGNvbHVtbnMgd2Ugd2FudCB0byBsb29rIGF0LiBJbiB0aGlzIGNhc2Ugd2UnbGwgYmUgZm9jdXNpbmcgb25seSBvbiB0aHJlZSBpbmRpY2F0b3JzOiB0b3RhbCBjYXNlcywgaG9zcGl0YWxpemF0aW9ucyBhbmQgY2FzZXMgYnJva2VuIGRvd24gaW50byBlaXRoZXIgbWFsZSBvciBmZW1hbGUgY2F0ZWdvcmllcy4NCjQuICBTdW1tYXJpemUgdGhlIGRhdGEgaW50byBhIHByb3BvcnRpb24gb2YgY2FzZXMgZm9yIGVhY2ggYWdlIGdyb3VwLCBiYXNlZCBvbiB0aGUgc3BlY2lmaWMgdG90YWwgZm9yIGVhY2ggaW5kaWNhdG9yIHdpdGhpbiBlYWNoIFBIVS4NCg0KQWZ0ZXIgYWxsIG9mIG91ciB0cmFuc2Zvcm1hdGlvbnMsIHdlJ2xsIGhhdmUgc3BlY2lmaWMgcHJvcG9ydGlvbnMgb2YgaW5mZWN0aW9ucywgZGVhdGhzLCBhbmQgaG9zcGl0YWxpemF0aW9ucyB3aXRoaW4gZWFjaCBQSFUgZm9yIGVhY2ggYWdlIGdyb3VwLg0KDQpgYGB7cn0NCiMgT3BlbiBvdXIgZGF0YXNldA0KY292aWRfZGVtb2dyYXBoaWNzLmRmIDwtIHJlYWRfY3N2KC4uLikNCg0KIyBUYWtlIGEgcXVpY2sgbG9vayBhdCBpdA0Kc3RyKGNvdmlkX2RlbW9ncmFwaGljcy5kZiwgZ2l2ZS5hdHRyID0gRkFMU0UpDQp0YWlsKGNvdmlkX2RlbW9ncmFwaGljcy5kZikNCmBgYA0KDQpgYGB7cn0NCiMgRm9ybWF0IG91ciBkYXRhc2V0IHRvIGZvY3VzIG9uIHRvdGFsX2Nhc2VzIGFuZCB0b3RhbF9kZWF0aHMNCmNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiA8LSANCmNvdmlkX2RlbW9ncmFwaGljcy5kZiAlPiUgDQoNCiMtLS0tLS0tLS0tIEZpeCBjb2x1bW4gbmFtZXMgLS0tLS0tLS0tLSMNCiMgY29udmVydCBjb2x1bW4gbmFtZXMgdG8gbG93ZXJjYXNlIGFuZCByZXBsYWNlIHNwYWNlcyB3aXRoICJfIi4gRG9lcyB0aGlzIGxvb2sgZmFtaWxpYXI/DQpyZW5hbWVfd2l0aCguLCB+IHN0cl90b19sb3dlcihzdHJfcmVwbGFjZV9hbGwoc3RyaW5nID0gLngsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSByIihccykiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJfIikpKSAlPiUgDQoNCiMgY2xlYW4gdXAgUEhVIG5hbWVzDQptdXRhdGUoZ2VvZ3JhcGhpY19hcmVhID0gc3RyX3JlbW92ZV9hbGwoZ2VvZ3JhcGhpY19hcmVhLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuPXIiKCheUHVibGljXHNIZWFsdGhccyl8KFxzUHVibGljLiopfChcc0hlYWx0aC4qKSkiKSwNCiAgICAgICBwZXJpb2QgPSBzdHJfcmVtb3ZlX2FsbChzdHJpbmcgPSBwZXJpb2QsIHBhdHRlcm4gPSByIihcc0NPVklELiopIikNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSANCg0KIy0tLS0tLS0tLS0gRmlsdGVyIGFuZCBmaXggdmFyaWFibGVzIC0tLS0tLS0tLS0jDQojIEZpbHRlciBvdXQgc29tZSBvZiB0aGUgZXhjZXNzIGRhdGENCmZpbHRlcihnZW9ncmFwaGljX2FyZWEgIT0gIk9udGFyaW8iLCAjIERyb3AgT250YXJpbyBkYXRhDQogICAgICAgYWdlX2dyb3VwICE9ICJBbGwgYWdlcyIpICU+JSAgIyBEcm9wIGNvbWJpbmVkIGFnZSBkYXRhDQoNCiMgUGFyZSBkb3duIHRoZSBkYXRhc2V0IHRvIGp1c3QgdG90YWxfY2FzZXMsIHRvdGFsX2RlYXRocywgYW5kIHRvdGFsX2hvc3BpdGFsaXphdGlvbnMNCnNlbGVjdChwZXJpb2QsIGZyb21fZGF0ZSwgdG9fZGF0ZSwgZ2VvZ3JhcGhpY19hcmVhLCBhZ2VfZ3JvdXAsIA0KICAgICAgIHRvdGFsX2Nhc2VzLCB0b3RhbF9yYXRlLCANCiAgICAgICB0b3RhbF9ob3NwaXRhbGl6YXRpb25zX2NvdW50LCB0b3RhbF9ob3NwaXRhbGl6YXRpb25zX3JhdGUsIA0KICAgICAgIG1hbGVfY2FzZXMsIGZlbWFsZV9jYXNlcykgJT4lIA0KDQojIENvbnZlcnQgdGhlIGFnZV9ncm91cCBpbnRvIGEgZmFjdG9yDQptdXRhdGUoYWdlX2dyb3VwID0gZmFjdG9yKGFnZV9ncm91cCksDQogICAgICAgIyBhZnRlciBjb252ZXJ0aW5nIHRvIGZhY3RvciwgdGhlICI1IHRvIDExIiB3aWxsIGJlIGluIHRoZSB3cm9uZyBwb3NpdGlvbi4gUmVsZXZlbCBhZ2VfZ3JvdXANCiAgICAgICBhZ2VfZ3JvdXAgPSBmY3RfcmVsZXZlbChhZ2VfZ3JvdXAsICI1IHRvIDExIiwgYWZ0ZXI9MSkpICU+JSANCg0KIyBSZW5hbWUgdGhlIGdlb2dyYXBoaWNfYXJlYSB2YXJpYWJsZQ0KcmVuYW1lKHB1YmxpY19oZWFsdGhfdW5pdCA9IGdlb2dyYXBoaWNfYXJlYSkgJT4lIA0KDQojLS0tLS0tLS0tLSBTdW1tYXJpemUgZGF0YSAtLS0tLS0tLS0tIw0KIyBHcm91cCB0aGUgZGF0YSBzbyB5b3UgY2FuIG1hbmlwdWxhdGUgYmFzZWQgb24gUEhVIGluIHRoZSBuZXh0IHN0ZXBzDQpncm91cF9ieShwZXJpb2QsIHB1YmxpY19oZWFsdGhfdW5pdCkgJT4lIA0KDQojIGdlbmVyYXRlIHBlcmNlbnQgdmFsdWVzIGZvciBlYWNoIGFnZSBncm91cCB3aXRoaW4gYSBQSFUNCm11dGF0ZShwZXJjZW50X2Nhc2VzID0gLi4uLCANCiAgICAgICAjIGdlbmVyYXRlIHBlcmNlbnQgaG9zcGl0YWxpemF0aW9ucyBmb3IgZWFjaCBhZ2UgZ3JvdXAgd2l0aGluIGEgUEhVDQogICAgICAgcGVyY2VudF9ob3NwaXRhbGl6YXRpb25zID0gdG90YWxfaG9zcGl0YWxpemF0aW9uc19jb3VudC9zdW0odG90YWxfaG9zcGl0YWxpemF0aW9uc19jb3VudCksDQogICAgICAgIyBnZW5lcmF0ZSBwZXJjZW50IG1hbGUgYW5kIGZlbWFsZSBjYXNlcyBmb3IgZWFjaCBhZ2UgZ3JvdXAgd2l0aGluIGEgUEhVDQogICAgICAgIyBXZSdsbCB1c2UgdGhpcyBkYXRhIGxhdGVyIQ0KICAgICAgIHBlcmNlbnRfbWFsZV9jYXNlcyA9IG1hbGVfY2FzZXMvKHRvdGFsX2Nhc2VzKSwNCiAgICAgICBwZXJjZW50X2ZlbWFsZV9jYXNlcyA9IGZlbWFsZV9jYXNlcy8odG90YWxfY2FzZXMpDQogICAgICApIA0KDQojIFRha2UgYSBsb29rIGF0IHRoZSBkaWZmZXJlbnQgYWdlIGRlbW9ncmFwaGljcw0KbGV2ZWxzKGNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiRhZ2VfZ3JvdXApDQpzdHIoY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmLCBnaXZlLmF0dHIgPSBGQUxTRSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNC4xLjAgRGVuc2l0eSBhbmQgaGlzdG9ncmFtIHBsb3RzIG1vZGVsIGRpc3RyaWJ1dGlvbiBvZiBzYW1wbGVzIG9yIGluZGl2aWR1YWxzIHdpdGhpbiBhIHBvcHVsYXRpb24NCg0KV2hpbGUgYmFyIHBsb3RzIGhlbHAgdG8gZm9jdXMgb24gcHJvcG9ydGlvbmFsIHJlcHJlc2VudGF0aW9uIGZvciBjYXRlZ29yaWNhbCBkYXRhLCBib3RoIGRlbnNpdHkgcGxvdHMgYW5kIGhpc3RvZ3JhbXMgY2FuIGJlIHVzZWQgdG8gY29udmV5IHRoZSBmcmVxdWVuY3kgb3IgZGlzdHJpYnV0aW9uIG9mIHZhbHVlcyBmb3IgYSB2YXJpYWJsZSBhY3Jvc3MgaXRzIHNwZWNpZmllZCByYW5nZS4gV2hlbiBjb21wYXJpbmcgZGlzdHJpYnV0aW9ucyB2aXN1YWxpemVkIHRoaXMgd2F5LCB5b3UgY2FuIHVzdWFsbHkgY29tcGFyZSB1cCB0byAzIG9yIDQgb24gdGhlIHNhbWUgcGxvdCBiZWZvcmUgdGhlIGRhdGEgYmVjb21lcyB0b28gY3Jvd2RlZC4gVGhlc2UgbWV0aG9kcyBhbHNvIGdpdmUgeW91IGEgc2Vuc2Ugb2YgeW91ciBkYXRhIGJlZm9yZSBtb3ZpbmcgZm9yd2FyZCB3aXRoIG1vcmUgZGV0YWlsZWQgYW5hbHlzZXMuIFlvdSBjYW4gYWxzbyBpZGVudGlmeSBwb3NzaWJsZSBtaXN0YWtlcyBvciBhcnRlZmFjdHMgaW4gZGF0YSBjb2xsZWN0aW9uLg0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LXdhcm5pbmd9DQoqKkhvdyBtdWNoIGRhdGEgZG8gSSBuZWVkPyoqIERlbnNpdHkgcGxvdHMgY2FuIGJlIHRob3VnaHQgb2YgYXMgc21vb3RoZWQgdmVyc2lvbnMgb2YgaGlzdG9ncmFtcyB3aGljaCBoYXZlIGJlZW4gYmlubmVkIGluIHNtYWxsIGludGVydmFscy4gKipEZW5zaXR5IHBsb3RzKiogb2YgYSBzaW5nbGUgZGltZW5zaW9uIHJlcXVpcmUgYSAqKiptaW5pbXVtIG9mIDQgc2FtcGxlcyoqKiBidXQganVzdGlmeWluZyBhIEtERSBvbiBhIHNhbXBsZSBzaXplIHRoYXQgc21hbGwgaXMgaGFyZC4gKipIaXN0b2dyYW1zKiogYXJlIHJlY29tbWVuZGVkIHRvIGhhdmUgKioqYXQgbGVhc3QgMzAgb2JzZXJ2YXRpb25zKioqIHRvIGJlIGNvbnNpZGVyZWQgdXNlZnVsIGFuZCBJIHdvdWxkIGFwcGx5IHRoaXMgcnVsZSBvZiB0aHVtYiB0byBLREVzIGFzIHdlbGwuDQo6OjoNCg0KTGV0J3MgcGljayBhIGZldyBhZ2UgZ3JvdXBzIHRvIHBsb3QgYmFzZWQgb24gYHBlcmNlbnRfY2FzZXNgLiBXZSdsbCB1c2UgZGF0YSBnYXRoZXJlZCBmcm9tIGVhY2ggUEhVIHRvIHNlZSBpZiB0aGUgc2FtZSBnZW5lcmFsIHRyZW5kcyAoaWYgYW55KSBhcHBseSByZWdhcmRsZXNzIG9mIFBIVS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCiAgIyBGaWx0ZXIgZm9yIGp1c3QgYSBmZXcgYWdlIGdyb3Vwcw0KICBmaWx0ZXIocGVyaW9kID09ICJjdW11bGF0aXZlIiwgDQogICAgICAgICBhZ2VfZ3JvdXAgJWluJSBjKCIwIHRvIDQiLCIxMiB0byAxOSIsICIyMCB0byAzOSIsICI4MCsiKSkgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHggPSAuLi4sIGZpbGwgPSAuLi4pICsNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2RlbnNpdHkoLi4uKSArICMjIGdlbmVyYXRlIG91ciBLREUNCiAgICAuLi4oYWVzKGNvbG91ciA9IGFnZV9ncm91cCkpICMjIGNvbmZpcm0gb3VyIGRhdGEgdmFsdWVzIHdpdGggYSBnZW9tX3J1Zw0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNC4xLjEgQXBwbHkgYSBgZmFjZXRfKigpYCB0byB2aWV3IEtERXMgc2VwYXJhdGVseQ0KDQpBcyB5b3UgY2FuIHNlZSBmcm9tIGFib3ZlLCBhcyB3ZSBzdGFydCB0byBoYXZlIG1vcmUgYW5kIG1vcmUgYWdlIGdyb3VwcywgaXQgbWF5IGJlIGJldHRlciB0byBzZXBhcmF0ZSB0aGVtIG91dCBpbiBvcmRlciB0byBhdm9pZCB0b28gbXVjaCBvdmVybGFwLiBJdCBtYXkgYWxzbyBiZSB0byBvdXIgYWR2YW50YWdlIHRvIGNvbXBhcmUgdGhlbSBpbiBhIG1vcmUgdmVydGljYWwgZmFzaGlvbi4gUmVjYWxsIHRoYXQgdGhlcmUgYXJlIHR3byBsYXllcnMgd2UgY2FuIGNob29zZSBmcm9tOiBgZmFjZXRfd3JhcCgpYCBhbmQgYGZhY2V0X2dyaWQoKWAuIFRoZXkgYXJlIGRpZmZlcmVudGlhdGVkIGJ5IHRoZSBmb2xsb3dpbmcgY2hhcmFjdGVyaXN0aWNzOg0KDQotICAgYGZhY2V0X3dyYXAoKWA6IERhdGEgaXMgc3BsaXQgYmFzZWQgb24gYXZhaWxhYmxlIGRhdGEgY29tYmluYXRpb25zIG9mIHRoZSBgZmFjZXRzYCBwYXJhbWV0ZXIgd2hpY2ggY2FuIGJlIGRlZmluZWQgYnkgYSBmb3JtdWxhIGxpa2UgYH52YXIxK3ZhcjJgIG9yIGJ5IHVzaW5nIHRoZSBxdW90aW5nIGZ1bmN0aW9uIGB2YXJzKHZhcjEsIHZhcjIpYC4gSW4gZWl0aGVyIGNhc2UsIHRoZSBkYXRhIGlzIHRoZW4gKmdyb3VwZWQgYnkqIHlvdXIgdmFyaWFibGVzLiBQYW5lbHMgYXJlIHBsYWNlZCBpbiBhIHNpbmdsZSByaWJib24gdGhhdCB3cmFwcyBhcm91bmQgYmFzZWQgb24gdGhlIGFyZ3VtZW50cyBpbiB0aGUgYG5jb2xgIGFuZCBgbnJvd2AgcGFyYW1ldGVycy4NCi0gICBgZmFjZXRfZ3JpZCgpYDogRGF0YSBpcyBzcGxpdCBiYXNlZCBvbiB0aGUgKioqMSBvciAyLWRpbWVuc2lvbmFsKioqIGNvbWJpbmF0aW9uIG9mIGZhY2V0IHZhcmlhYmxlcy4gRXZlbiBjb21iaW5hdGlvbnMgZm9yIHdoaWNoIHRoZXJlIGlzIG5vIGRhdGEgd2lsbCBiZSBkaXNwbGF5ZWQgYXMgZW1wdHkgcGFuZWxzLiBVc2UgdGhlIGByb3dzYCBhbmQgYGNvbHNgIHBhcmFtZXRlcnMgdG8gc2V0IHRoZSBmYWNldHMgYnkgdXNpbmcgdGhlIGB2YXJzKClgIHF1b3RpbmcgZnVuY3Rpb24uIE5vdGUgdGhhdCB5b3UgY291bGQgZnVydGhlciBncm91cCB5b3VyIHJvd3MgYW5kL29yIGNvbHVtbnMgYnkgKm11bHRpcGxlIHZhcmlhYmxlcyouDQoNCk5vdGUgd2UnbGwgYWxzbyB1c2UgYW5vdGhlciBwYXJhbWV0ZXIgaW4gYm90aCBjYWxsZWQgYHNjYWxlc2Agd2hlcmUgd2UgY2FuIGRldGVybWluZSBpZiBwYW5lbHMgc2hvdWxkIHNoYXJlIGF4aXMgc2NhbGVzIG9yIGNoYW5nZSB0aGVtIGJhc2VkIG9uIGluZGl2aWR1YWwgcGFuZWwgZGF0YSBpbiB0aGUgeS1heGlzIChgZnJlZV95YCksIHgtYXhpcyAoYGZyZWVfeGApIG9yIGJvdGggKGBmcmVlYCksDQoNCkxldCdzIGZpcnN0IHVzZSBgZmFjZXRfd3JhcCgpYCB0byBnZW5lcmF0ZSBhIHNpbmdsZS1jb2x1bW4gZmFjZXQgb2YgS0RFcyBiYXNlZCBvbiB0aGUgY3VtdWxhdGl2ZSBwZXJpb2QgZnJvbSBvdXIgZGF0YS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0NCg0KY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCiAgIyBmaWx0ZXIgZm9yIG9ubHkgY3VtdWxhdGl2ZSBkYXRhDQogIGZpbHRlcihwZXJpb2QgPT0gImN1bXVsYXRpdmUiKSAlPiUgDQogICMgU2VsZWN0IGZvciBqdXN0IHRoZSBpbXBvcnRhbnQgY29sdW1ucw0KICBzZWxlY3QocHVibGljX2hlYWx0aF91bml0LCBhZ2VfZ3JvdXAsIHBlcmNlbnRfY2FzZXMpICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4ID0gcGVyY2VudF9jYXNlcywgZmlsbCA9IGFnZV9ncm91cCkgKw0KICANCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpICsgIyBzZXQgdGV4dCBzaXplDQogIA0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjUpICsgDQogIA0KICAgICMgNi4gRmFjZXRzDQogICAgIyMjIDQuMS4xIEFkZCBhIHNpbXBsZSBmYWNldCB3cmFwLCB3aXRoIGVhY2ggYWdlIGdyb3VwIGV4aXN0aW5nIGluIGl0cyBvd24gcm93DQogICAgZmFjZXRfd3JhcCguLi4sIA0KICAgICAgICAgICAgICAgc2NhbGVzPS4uLiwgDQogICAgICAgICAgICAgICBuY29sPS4uLikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDQuMS4yIFVzZSBgZmFjZXRfZ3JpZCgpYCB0byB2aWV3IGJvdGggcGVyaW9kcyBvZiBvdXIgZGF0YQ0KDQpJZiwgZm9yIGV4YW1wbGUgd2Ugd2FudGVkIHRvIGRpcmVjdGx5IGNvbXBhcmUgdGhlIGRhdGEgZnJvbSBib3RoIHBlcmlvZHMgKHJlY2VudCBhbmQgY3VtdWxhdGl2ZSksIHdlIGNvdWxkIGFsdGVyIHRoZSBhYm92ZSBwbG90IHNvIHRoYXQgb3VyIHR3byBwZXJpb2RzIGFyZSBwYW5lbGVkIGJlc2lkZSBlYWNoIG90aGVyLiBUaGlzIGNhbiBiZSBhY2NvbXBsaXNoZWQgd2l0aCB0aGUgYGZhY2V0X2dyaWQoKWAgbGF5ZXIgd2hpY2ggd2lsbCBhbGxvdyB1cyB0byBuYW1lIGJvdGggYSBgcm93c2AgYW5kIGBjb2xzYCBncm91cGluZyBwYXJhbWV0ZXIuDQoNCkxldCdzIHVwZGF0ZSBvdXIgZmlndXJlIHNvIHRoYXQgd2Ugc3BsaXQgb3VyIHJvd3MgYnkgYGFnZV9ncm91cGAgYW5kIG91ciBjb2x1bW5zIGJ5IGBwZXJpb2RgLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQ0KDQpjb3ZpZF9kZW1vZ3JhcGhpY3NfdG90YWwuZGYgJT4lIA0KICAjIFNlbGVjdCBmb3IganVzdCB0aGUgaW1wb3J0YW50IGNvbHVtbnMNCiAgc2VsZWN0KHBlcmlvZCwgcHVibGljX2hlYWx0aF91bml0LCBhZ2VfZ3JvdXAsIHBlcmNlbnRfY2FzZXMpICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4ID0gcGVyY2VudF9jYXNlcywgZmlsbCA9IGFnZV9ncm91cCkgKw0KICANCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpICsgIyBzZXQgdGV4dCBzaXplDQogIA0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjUpICsgDQogIA0KICAgICMgNi4gRmFjZXRzDQogICAgIyMgVXNlIGEgZmFjZXRfZ3JpZCB0byBwYW5lbCBkYXRhIGJ5IG11bHRpcGxlIHZhcmlhYmxlcw0KICAgIGZhY2V0X2dyaWQocm93cyA9IC4uLiwgDQogICAgICAgICAgICAgICBjb2xzID0gLi4uLA0KICAgICAgICAgICAgICAgc2NhbGVzPSJmcmVlIikgIyBBbGxvdyBhbGxvdyBzY2FsZXMgdG8gYWx0ZXIgYmV0d2VlbiByb3dzLCBhbmQgYmV0d2VlbiBjb2x1bW5zLg0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpXaGlsZSB0aGUgYWJvdmUgdmlzdWFsaXphdGlvbiBpcyBwcmV0dHkgY2xlYXIsIGl0IGNlcnRhaW5seSB0YWtlcyB1cCBhIGxvdCBvZiBleHRyYSBzcGFjZS4gUGVyaGFwcyB0aGVyZSBpcyBhIGJldHRlciB3YXkgdG8gZ2VuZXJhdGUgdGhpcyBraW5kIG9mIHZpc3VhbGl6YXRpb24/DQoNCiMjIDQuMi4wIFBsb3QgbXVsdGlwbGUgZGlzdHJpYnV0aW9ucyB3aXRoIGEgcmlkZ2VsaW5lIHBsb3QNCg0KUmlkZ2VsaW5lIHBsb3RzIChzb21ldGltZXMgY2FsbGVkIEpveXBsb3RzKSBjYW4gZ2VuZXJhdGUgYSBjb21wYWN0IHdheSB0byBzaG93IG11bHRpcGxlIGRpc3RyaWJ1dGlvbnMgb2YgbnVtZXJpYyB2YWx1ZXMgYWNyb3NzIHNldmVyYWwgZ3JvdXBzLiBUaGUgZGlzdHJpYnV0aW9ucyBjYW4gYmUgc2hvd24gYXMgZWl0aGVyIGhpc3RvZ3JhbXMgb3IgZGVuc2l0eSBwbG90cyB3aXRoIGFsbCBvZiB0aGVtIGFsaWduZWQgdG8gdGhlIHNhbWUgaG9yaXpvbnRhbCBheGlzLiBUaGUgdmVydGljYWwgYXhpcyBpcyBjb21wcmVzc2VkIHNsaWdodGx5IHRvIGdlbmVyYXRlIGFuIG92ZXJsYXAgYmV0d2VlbiBjYXRlZ29yaWVzLg0KDQpUbyBnZW5lcmF0ZSB0aGVzZSB2aXN1YWxpemF0aW9ucyB3ZSBjYW4gdXNlIHRoZSBgZ2dyaWRnZXNgIHBhY2thZ2Ugd2hpY2ggaXMgYW4gZXh0ZW5zaW9uIG9mIGBnZ3Bsb3QyYC4gSW4gdGhpcyBjYXNlLCB0aGF0IG1lYW5zIGl0IHVzZXMgdGhlIHNhbWUgZ3JhbW1hciBhbmQgY2FuIGJlIGFkZGVkIGFzIGEgbGF5ZXIgY2FsbCB0byBgZ2VvbV9kZW5zaXR5X3JpZGdlcygpYC4gQSBwYXJhbWV0ZXIgdG8ga2VlcCBpbiBtaW5kOg0KDQotICAgYHNjYWxlYDogc2V0cyB0aGUgdmVydGljYWwgZGlzdGFuY2UgYmV0d2VlbiByaWRnZWxpbmVzDQoNCiAgICAtICAgMTogdGhlIHRhbGxlc3QgZGVuc2l0eSBjdXJ2ZSBqdXN0IHRvdWNoZXMgdGhlIGJhc2VsaW5lIG9mIHRoZSBuZXh0IHZlcnRpY2FsIGNhdGVnb3J5DQoNCiAgICAtICAgQWJvdmUgMTogaW5jcmVhc2luZyBvdmVybGFwDQoNCiAgICAtICAgQmVsb3cgMTogaW5jcmVhc2luZyBzZXBhcmF0aW9uDQoNCkxldCdzIGJlZ2luIGJ5IHJlcGxpY2F0aW5nIG91ciBmYWNldGVkIHBsb3QgZnJvbSBhYm92ZSB3aGljaCBjb21wYXJlcyB0aGUgKmN1bXVsYXRpdmUgdnMgcmVjZW50KiBkYXRhIGFjcm9zcyBhZ2UgZ3JvdXBzLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQpjb3ZpZF9kZW1vZ3JhcGhpY3NfdG90YWwuZGYgJT4lIA0KICAjIFNlbGVjdCBmb3IganVzdCB0aGUgaW1wb3J0YW50IGNvbHVtbnMNCiAgc2VsZWN0KHBlcmlvZCwgcHVibGljX2hlYWx0aF91bml0LCBhZ2VfZ3JvdXAsIHBlcmNlbnRfY2FzZXMpICU+JSANCiAgDQogICMgUGxvdCBhIHJpZGdlbGluZSBwbG90DQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4ID0gcGVyY2VudF9jYXNlcywgeSA9IGFnZV9ncm91cCwgZmlsbCA9IGFnZV9ncm91cCkgKw0KICANCiAgICB0aGVtZV9idygpICsgIyBTaW1wbGlmeSB0aGUgdW5kZXJseWluZyB0aGVtZQ0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgIyBkcm9wIHRoZSBsZWdlbmQgc2luY2UgaXQncyByZWR1bmRhbnQNCiAgDQogICAgIyA0LiBHZW9tcw0KICAgICMjIyA0LjIuMCBBZGQgYSByaWRnZWxpbmUgcGxvdCBpbnN0ZWFkIG9mIEtERQ0KICAgIC4uLiArIA0KICANCiAgICAjIDYuIEZhY2V0cw0KICAgICMjIyA0LjIuMCBNYWtlIHBhbmVscyB0byBkaXNwbGF5IG91ciBwZXJpb2QgZGF0YQ0KICAgIGZhY2V0X2dyaWQoLi4uLCBzY2FsZXMgPSAiZnJlZV94IikNCiAgICANCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNC4zLjAgVXNlIGBnZW9tX2RlbnNpdHlfcmlkZ2VzX2dyYWRpZW50KClgIHRvIGZpbGwgZGVuc2l0aWVzIG9uIGEgZ3JhZGllbnQNCg0KRnJvbSBhYm92ZSB5b3UgY2FuIG5vdyBzZWUgdGhhdCB3ZSd2ZSBzb21ld2hhdCBjb21wYWN0ZWQgYWxsIHRoYXQgZGltZW5zaW9uYWwgZGF0YSBpbnRvIGEgc2luZ2xlIHBsb3QgdGhhdCBzdGlsbCBjbGVhcmx5IGNvbnZleXMgdGhlIGRpZmZlcmVuY2UgaW4gb3ZlcmFsbCBwcm9wb3J0aW9ucyBmb3IgdG90YWwgaW5mZWN0aW9ucyB3aXRoaW4gZWFjaCBhZ2UgZ3JvdXAuIFRoZSBkaXN0cmlidXRpb25zIGFjcm9zcyBvdXIgY2F0ZWdvcmllcyBzdWdnZXN0IHRoYXQgdGhlIDIwLTM5IGFnZSBncm91cCBtYWtlcyB1cCBhIGxhcmdlciBwcm9wb3J0aW9uIG9mIG92ZXJhbGwgY3VtdWxhdGl2ZSBjYXNlcyB3aXRoaW4gZWFjaCBQSFUuIE9uIHRoZSBvdGhlciBoYW5kLCB0aGUgMjAtODAgYWdlIHJhbmdlcyBhbGwgYXBwZWFyIHRvIGhhdmUgc2ltaWxhciBkaXN0cmlidXRpb25zIG9mIGNhc2VzIGluIHRoZSBtb3N0IHJlY2VudCBkYXRhLCBhbHRob3VnaCB0aGF0IG1heSBiZSBhIGxlc3MgcmVsaWFibGUgaW5kaWNhdG9yIG9mIHRoZSB0cnVlIGNhc2Ugc3ByZWFkLg0KDQpGb3Igb3VyIGF1ZGllbmNlLCB3ZSB3b3VsZCBuZWVkIHRvIGNsZWFuIHVwIG91ciBheGlzIHRpdGxlcyB0byBjbGFyaWZ5IHRoYXQgdGhlc2UgcHJvcG9ydGlvbnMgYXJlIGNhbGN1bGF0ZWQgaW5kZXBlbmRlbnRseS4gQW4gYWRkaXRpb25hbCBvcHRpb24gd2l0aCB5b3VyIHJpZGdlbGluZSBwbG90cyBpcyB0aGUgZmlsbCB2YXJpYW50LiBUbyBhY2NvbXBsaXNoIGEgbmljZXIgZ3JhZGllbnQgd2Ugd2lsbCBpbmNsdWRlIGEgY2FsbCB0byBgc2NhbGVfZmlsbF92aXJpZGlzX2NgIHNpbmNlIG91ciB4LWF4aXMgaXMgY29udGludW91cy4gS2VlcCBpbiBtaW5kIHRoYXQgd2UgYWxzbyBjYW5ub3Qgc2V0IHRoZSBgYWxwaGFgIHRyYW5zcGFyZW5jeSBvbiBvdXIgZGVuc2l0eSBwbG90cyB3aGVuIGZpbGxpbmcgd2l0aCBhIGdyYWRpZW50LiBXZSBhbHNvIGhhdmUgdG8gc2V0IG91ciBhZXN0aGV0aWNzIGBmaWxsYCB0byBgc3RhdCh4KWAgdG8gYWNjb21wbGlzaCB0aGlzIGZlYXQgYXMgd2VsbC4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0NCg0KY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCiAgIyBTZWxlY3QgZm9yIGp1c3QgdGhlIGltcG9ydGFudCBjb2x1bW5zDQogIHNlbGVjdChwZXJpb2QsIHB1YmxpY19oZWFsdGhfdW5pdCwgYWdlX2dyb3VwLCBwZXJjZW50X2Nhc2VzKSAlPiUgDQogIA0KICAjIFBsb3QgYSByaWRnZWxpbmUgcGxvdA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeCA9IHBlcmNlbnRfY2FzZXMsIHkgPSBhZ2VfZ3JvdXAsIGZpbGwgPSBzdGF0KHgpKSArDQogIA0KICAgIHRoZW1lX2J3KCkgKyAjIFNpbXBsaWZ5IHRoZSB1bmRlcmx5aW5nIHRoZW1lDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArICMgc2V0IHRleHQgc2l6ZQ0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyAjIGRyb3AgdGhlIGxlZ2VuZCBzaW5jZSBpdCdzIHJlZHVuZGFudA0KICANCiAgICAjIDMuIFNjYWxpbmcNCiAgICAjIyMgNC4zLjAgVXNlIHRoZSBNYWdtYSBvcHRpb24gDQogICAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZT0iUGVyY2VudCBvZiBQSFUiLCBvcHRpb24gPSAiQyIpICsgDQogIA0KICAgICMgNC4gR2VvbXMNCiAgICAjIyBVc2UgYSBncmFkaWVudCB2ZXJzaW9uIG9mIHJpZGdlcyBpbnN0ZWFkDQogICAgLi4uICsgDQogIA0KICAgICMgNi4gRmFjZXRzDQogICAgZmFjZXRfZ3JpZCh+cGVyaW9kLCAgICAgICAgICAgIyBFcXVpdmFsZW50IHRvIHJvdyA9IHZhcnMocGVyaW9kKQ0KICAgICAgICAgICAgICAgc2NhbGVzID0gImZyZWVfeCIpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgNS4wLjAgQ2F0ZWdvcmljYWwgZGlzdHJpYnV0aW9uIHBsb3RzDQoNCkRpZCB5b3Uga25vdyB0aGUgYm94cGxvdCBpcyBuZWFybHkgNTAgeWVhcnMgb2xkISBGaXJzdCBpbnZlbnRlZCBpbiB0aGUgMTk3MHMgYnkgb3VyIGZhdm91cml0ZSBzdGF0aXN0aWNpYW4sIEpvaG4gV2lsZGVyIFR1a2V5LCB3ZSdsbCBkaWcgaW50byBob3cgYW5kIHdoZW4gdG8gdXNlIHRoaXMgaWNvbmljIHBsb3QuIFdoaWxlIHdlJ3JlIGhlcmUgd2UnbGwgYWxzbyB0YWtlIGEgbG9vayBhdCBvdGhlciBjYXRlZ29yaWNhbCBkaXN0cmlidXRpb24gcGxvdHMuIFdoaWxlIG91ciBLREUgYW5kIHJpZGdlbGluZSBwbG90cyBwcm92aWRlIHF1aXRlIGEgYml0IG9mIGRldGFpbCwgdGhleSBjYW4gYWxzbyBiZSBhIGxpdHRsZSBtb3JlIGxpbWl0ZWQgaW4gdGhlaXIgc3BhY2UgZWZmaWNpZW5jeS4gVGhlIGZvbGxvd2luZyBjYXRlZ29yaWNhbCBkaXN0cmlidXRpb24gcGxvdHMgd2lsbCBwZXJoYXBzIHByb3ZpZGUgc29tZSBtb3JlIGluZm9ybWF0aW9uIGVmZmljaWVuY3kuDQoNCiMjIDUuMS4wIFN1bW1hcml6ZSBwb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbnMgd2l0aCBgZ2VvbV9ib3hwbG90KClgDQoNCkFzIHlvdSBjYW4gc2VlIGZyb20gdGhlIHByZXZpb3VzIHNlY3Rpb24sIHdlIGNvbWZvcnRhYmx5IGZpdCBhIHF1aXRlIGZldyBkaXN0cmlidXRpb25zIG9uIGEgcmlkZ2VsaW5lIHBsb3QuIEZyb20gdGhlIGxvb2tzIG9mIGl0LCB0aGUgMjAtMzkgYWdlIGdyb3VwIGxvb2tzIHRvIG1ha2UgdXAgYSBoaWdoZXIgcGVyY2VudCBvZiBjYXNlcyBhY3Jvc3MgYWxsIHRoZSBQSFVzLiBQcmV2aW91c2x5IHRoaXMgZGF0YSB3YXMgYnJva2VuIGRvd24gYnkgMTAteWVhciBncm91cGluZ3MgYnV0IGl0IGhhcyBzaW5jZSBiZWVuIGFtZW5kZWQgdG8gbWFrZSBsYXJnZXIgYWdlIGdyb3VwcyBpbiB0aGUgMjArIHJhbmdlLiBTdGlsbCwgd2UgY2FuIHN0aWxsIGV4cGxvcmUgdGhpcyBkYXRhIGEgbGl0dGxlIGNsb3Nlci4NCg0KQ2FuIHdlIHZpc3VhbGl6ZSB0aGUgZGF0YSBpbiBhIG1vcmUgc3VtbWFyaXplZCBmb3JtPyBMZXQncyBleHBsb3JlIHRoZSBib3ggcGxvdC4NCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9ib3hwbG90X2Rpc3NlY3Rpb24ucG5nP3Jhdz10cnVlIiB3aWR0aD0iODAwIi8+DQoNClRoZSBkaXNzZWN0aW9uIG9mIGEgYm94cGxvdCdzIGNvbXBvbmVudHMgc2hvd3MgdXMgaG93IGl0IHN1bW1hcml6ZXMgZGF0YSBkaXN0cmlidXRpb24uDQo6OjoNCg0KQWxzbyBrbm93biBhcyB0aGUgYm94IGFuZCB3aGlza2VyIHBsb3QsIHRoaXMgdmlzdWFsaXphdGlvbiBjb252ZXlzIHRoZSBkaXN0cmlidXRpb24gb2Ygc2FtcGxlcyB3aXRoaW4gYSBncm91cCBvciBwb3B1bGF0aW9uIGFuZCBpcyBidWlsdCB1cG9uIDUgcHJpbmNpcGFsIHZhbHVlczoNCg0KLSAgICJCb3giDQoNCiAgICAtICAgKiptZWRpYW4qKjogZGFyayBsaW5lIGFjcm9zcyBjZW50cmUgb2YgYm94DQoNCiAgICAtICAgKipsb3dlciBxdWFydGlsZSoqOiBsb3dlci1ib3VuZCBvZiBib3gNCg0KICAgIC0gICAqKnVwcGVyIHF1YXJ0aWxlKio6IHVwcGVyLWJvdW5kIG9mIGJveA0KDQotICAgIldoaXNrZXJzIg0KDQogICAgLSAgICoqbG93ZXIgZXh0cmVtZSoqOiBsZW5ndGggb2YgbG93ZXIgd2hpc2tlcg0KICAgIC0gICAqKnVwcGVyIGV4dHJlbWUqKjogbGVuZ3RoIG9mIHVwcGVyIHdoaXNrZXINCg0KVG9nZXRoZXIsIHRoZSBsb3dlciBhbmQgdXBwZXIgcXVhcnRpbGVzIHByb2R1Y2UgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UgKElRUikuIFRoZSBnZW5lcmFsIGltcGxlbWVudGF0aW9uIG9mIGJveHBsb3RzIGNsYXNzaWZ5IGFueSBvYnNlcnZhdGlvbnMgMS41IElRUiBhYm92ZSB0aGUgdXBwZXIgcXVhcnRpbGUgb3IgYmVsb3cgdGhlIGxvd2VyIHF1YXJ0aWxlIGFzICoqb3V0bGllcnMqKiBvZiB0aGUgZGlzdHJpYnV0aW9uLiBUaGUgY2hhcmFjdGVyaXN0aWNzIG9mIG91dGxpZXJzIGNhbiBiZSBzZXQgYXMgcGFyYW1ldGVycyB3aXRoaW4gdGhlIGBnZW9tX2JveHBsb3QoKWAgbGF5ZXIuIFBhcmFtZXRlcnMgaW5jbHVkZSBgb3V0bGllci5zaGFwZWAsIGBvdXRsaWVyLnNpemVgLCBhbmQgYG91dGxpZXIuY29sb3VyYC4NCg0KVW5saWtlIGEgaGlzdG9ncmFtLCB0aGUgKiptaW5pbXVtKiogbnVtYmVyIG9mIHZhbHVlcyB0byBnZW5lcmF0ZSBhIGJveHBsb3QgaXMgKio1KiouIFdoaWxlIHlvdSBjb3VsZCBnZW5lcmF0ZSBhIGJveHBsb3Qgb24gZmV3ZXIgbnVtYmVycywgeW91IG1pZ2h0IG5vdCBoYXZlIGFjdHVhbCB3aGlza2VycyEgVGhpcyBpcyBkZWZpbml0ZWx5IGEgZ3JlYXQgYWx0ZXJuYXRpdmUgd2hlbiBzYW1wbGUgc2l6ZXMgYXJlIGJldHdlZW4gNS0zMCBmb3IgZWFjaCBwb3B1bGF0aW9uLg0KDQoqSGlzdG9yaWNhbGx5KiB0aGlzIHdhcyBhIHNpbXBsZSB3YXkgdG8gdmlzdWFsaXplIHN1bW1hcnkgc3RhdGlzdGljcyBvZiBwb3B1bGF0aW9uIHdoaWxlIGJlaW5nIGVhc3kgdG8gcHJvZHVjZSBieSBoYW5kLiBPZiBjb3Vyc2UsIHdpdGggdGhlIGFnZSBvZiBjb21wdXRpbmcsIHRoZSBwcm9kdWN0aW9uIG9mIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlcyBoYXZlIGFsbG93ZWQgZm9yIG1vcmUgZGl2ZXJzZSB2aXN1YWxpemF0aW9ucy4gVGhpcyBwbG90LCBob3dldmVyLCByZW1haW5zIGEgcG9wdWxhciBmb3JtYXQgYW5kIHRodXMgaXMgbW9yZSByZWFkaWx5IHVuZGVyc3Rvb2QgYnkgZ2VuZXJhbCBhdWRpZW5jZXMuDQoNCkVhY2ggY29tcGFjdCBib3ggY2FuIHRha2UgdXAgdGhlIHNhbWUgc3BhY2UgYXMgYSBiYXJwbG90IGNvbHVtbiBidXQgaXQgZ2l2ZXMgbXVjaCBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBwb3B1bGF0aW9uLiBMZXQncyBsb29rIGF0IGEgc2luZ2xlIGFzcGVjdCAtIGBwZXJjZW50X2Nhc2VzYCBpbiB0aGUgY3VtdWxhdGl2ZSBgcGVyaW9kYC4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBHZW5lcmF0ZSBhIGJhc2ljIGJveCBwbG90IHdpdGggb3V0bGllcnMgcHJlc2VudA0KY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCiAgIyBGaWx0ZXIgZm9yIGN1bXVsYXRpdmUgZGF0YQ0KICBmaWx0ZXIocGVyaW9kID09ICJjdW11bGF0aXZlIikgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHg9Li4uLCB5ID0gLi4uKSArDQoNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpICsgIyBzZXQgdGV4dCBzaXplDQoNCiAgICAjIDQuIEdlb21zDQogICAgLi4uICMjIyA1LjEuMCBBZGQgdGhlIGJveHBsb3QgZ2VvbQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA1LjIuMCBVcGdyYWRlIHlvdXIgYm94cGxvdCB3aXRoIHNvbWUgY29uZmlkZW5jZSBpbnRlcnZhbHMgYW5kIHZhbHVlcw0KDQpGcm9tIHRoZSBsb29rcyBvZiBpdCwgd2UgY2FuIGNvbmZpcm0gd2hhdCB3ZSBzYXcgYmVmb3JlIGluIG91ciBkZW5zaXR5IHBsb3QgLSB0aGF0IHRoZSAyMC0zOSBhZ2UgZ3JvdXAgZ2VuZXJhdGVzIHRoZSBoaWdoZXN0IHBlcmNlbnRhZ2Ugb2YgY2FzZXMgYWNyb3NzIFBIVXMgYW5kIHRoYXQgd2l0aCBpbmNyZWFzaW5nIGFnZSwgdGhlIG51bWJlciBvZiByZXBvcnRlZCBjYXNlcyBkZWNyZWFzZXMgYXMgYSBwcm9wb3J0aW9uIG9mIHRoZSB0b3RhbC4NCg0KV2UgY2FuIGFkZCBhIGZldyBleHRyYSBpdGVtcyB0byB0aGUgcGxvdCB0byBoZWxwIHVzIHZpc3VhbGl6ZSB0aGUgZGF0YToNCg0KLSAgIGFkZCBvdXIgZGF0YSBwb2ludHMgd2l0aCBgZ2VvbV9qaXR0ZXIoKWAgYW5kIHJlbW92ZSBvdXRsaWVycyBmcm9tIHRoZSBib3hwbG90IHRvIGF2b2lkIGRvdWJsZS1wbG90dGluZyBwb2ludHMuDQoNCi0gICBtYXJrIGEgXH45NSUgY29uZmlkZW5jZSBpbnRlcnZhbCB3aXRoaW4gdGhlIHNoYXBlIG9mIGVhY2ggYm94IHBsb3Qgd2l0aCB0aGUgYG5vdGNoYCBwYXJhbWV0ZXIuDQoNCi0gICB5b3UgY2FuIHNldCBhIHZhcmlhYmxlIHdpZHRoIGZvciBlYWNoIGJveHBsb3QgdGhhdCBpcyBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgdXNlZCB0byBnZW5lcmF0ZSB0aGUgcGxvdC4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBHZW5lcmF0ZSBhIGJhc2ljIGJveCBwbG90IHdpdGggb3V0bGllcnMgcHJlc2VudA0KY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCiAgIyBGaWx0ZXIgZm9yIGN1bXVsYXRpdmUgZGF0YQ0KICBmaWx0ZXIocGVyaW9kID09ICJjdW11bGF0aXZlIikgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHg9YWdlX2dyb3VwLCB5ID0gcGVyY2VudF9jYXNlcykgKw0KICANCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpICsgIyBzZXQgdGV4dCBzaXplDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIA0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZT0uLi4sICAjIyMgNS4yLjAgUmVtb3ZlIG91dGxpZXJzIA0KICAgICAgICAgICAgICAgICBub3RjaCA9IC4uLikgKyAgICAjIyMgNS4yLjAgQWRkIGEgY29uZmlkZW5jZSBpbnRlcnZhbCBub3RjaA0KICANCiAgICAjIyMgNS4yLjAgQWRkIGRhdGFwb2ludHMNCiAgICAuLi4NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNS4zLjAgYGdlb21fYmVlc3dhcm1gIHRha2VzIHBsb3R0aW5nIHlvdXIgcG9pbnRzIHVwIHRvIHRoZSBuZXh0IGxldmVsDQoNCkFzIHlvdSBjYW4gc2VlIGZyb20gYWJvdmUgdGhlIGBnZW9tX2ppdHRlcigpYCBsYXllciBkb2VzIGFkZCBwb2ludHMgdG8gb3VyIGJveHBsb3QgYnkgcGxvdHRpbmcgdGhlIHBvaW50cyBzdWNoIHRoYXQgdGhleSBhdm9pZCBvdmVybGFwcGluZyBhcyBtdWNoIGFzIHBvc3NpYmxlLiBQb2ludHMgYXJlIHJlc3RyaWN0ZWQgdG8gdGhlIHdpZHRoIG9mIHRoZSBib3hwbG90IGFsdGhvdWdoIHRoaXMgY2FuIGFsc28gYmUgYWRqdXN0ZWQgdG8gc29tZSBkZWdyZWUgd2l0aCB0aGUgcmlnaHQgcGFyYW1ldGVycy4gYGdlb21faml0dGVyKClgIGlzIG5hdGl2ZSB0byB0aGUgYGdncGxvdDJgIHBhY2thZ2Ugd2l0aCBzb21lIHBhcmFtZXRlcnMgdGhhdCBhbGxvdyBmb3IgYSBtb3JlICJyYW5kb20iIGRpc3RyaWJ1dGlvbiBvZiB5b3VyIGRhdGEgcG9pbnRzIHdpdGhpbiBhIHByb3ZpZGVkIGFyZWEuDQoNClRoZSBnb2FsIG9mIHRoZSBgZ2diZWVzd2FybWAgcGFja2FnZSBpcyB0byBnZW5lcmF0ZSBwb2ludHMgdGhhdCB3aWxsIG5vdCBvdmVybGFwIGJ1dCB0aGV5ICpjYW4qIGFsc28gYmUgdXNlZCB0byBzaW11bHRhbmVvdXNseSBzaW11bGF0ZSB0aGUga2VybmVsIGRlbnNpdHkgb2YgeW91ciBkYXRhLiBUaGVyZSBhcmUgdHdvIGdlb21zIHN1cHBsaWVkIHRoYXQgd29yayB3aXRoIHRoZSBgZ2dwbG90MmAgcGFja2FnZSB0byBhY2NvbXBsaXNoIHRoaXM6DQoNCmBnZW9tX2JlZXN3YXJtKClgIGhhcyBhIG51bWJlciBvZiBwYXJhbWV0ZXJzIHRoYXQgY2FuIGJlIHVzZWQgdG8gc2V0IHRoZWlyIGBhZXMoKWAgbWFwcGluZ3MgYnV0IGFsc28gaG93IHRoZSBwb2ludHMgYXJlIGxhaWQgb3V0Lg0KDQotICAgYHByaW9yaXR5YDogZGV0ZXJtaW5lcyB0aGUgbWV0aG9kIHVzZWQgdG8gcGVyZm9ybSB0aGUgcG9pbnQgbGF5b3V0Lg0KDQogICAgLSAgIG9wdGlvbnMgaW5jbHVkZTogYXNjZW5kaW5nLCBkZXNjZW5kaW5nLCBkZW5zaXR5LCByYW5kb20sIGFuZCBub25lLg0KDQotICAgYGdyb3VwT25YYDogaWYgVFJVRSB0aGVuIGppdHRlciBpcyBhZGRlZCB0byB0aGUgeCBheGlzIChkZWZhdWx0IGJlaGF2aW91cikgb3RoZXJ3aXNlIGppdHRlciBhbG9uZyB0aGUgeSBheGlzLg0KDQotICAgYGRvZGdlLndpZHRoYDogdGhlIGFtb3VudCBieSB3aGljaCBkaWZmZXJlbnQgYWVzdGhldGljcyBncm91cHMgKG11c3QgYmUgYSBmYWN0b3IpIHdpbGwgYmUgZG9kZ2VkLg0KDQotICAgYHNob3cubGVnZW5kYDogZGV0ZXJtaW5lcyBvZiB0aGUgdGhpcyBsYXllciBzaG91bGQgYmUgaW5jbHVkZWQgaW4gdGhlIGxlZ2VuZHMuDQoNCiAgICAtICAgb3B0aW9ucyBpbmNsdWRlOiBgTkFgICh5ZXMgaWYgYWVzdGhldGljcyBhcmUgbWFwcGVkKSwgYEZBTFNFYCAobmV2ZXIgaW5jbHVkZSksIGFuZCBgVFJVRWAgKGFsd2F5cyBpbmNsdWRlKQ0KDQotICAgYGNleGA6IGFuIG9wdGlvbmFsIHBhcmFtZXRlciB0aGF0IGNhbiBiZSB1c2VkIHRvIGhlbHAgc2V0IHRoZSBzcGFjaW5nIGJldHdlZW4gdmFsdWVzLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQojIEdlbmVyYXRlIGEgYmFzaWMgYm94IHBsb3Qgd2l0aCBvdXRsaWVycyBwcmVzZW50DQpjb3ZpZF9kZW1vZ3JhcGhpY3NfdG90YWwuZGYgJT4lIA0KICAjIEZpbHRlciBmb3IgY3VtdWxhdGl2ZSBkYXRhDQogIGZpbHRlcihwZXJpb2QgPT0gImN1bXVsYXRpdmUiKSAlPiUgDQoNCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHg9YWdlX2dyb3VwLCB5ID0gcGVyY2VudF9jYXNlcykgKw0KDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArICMgc2V0IHRleHQgc2l6ZQ0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlPU5BLCAgIyBSZW1vdmUgb3V0bGllcnMgDQogICAgICAgICAgICAgICAgIG5vdGNoID0gVFJVRSkgKyAgICAjIEFkZCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgbm90Y2gNCg0KICAgICMjIyA1LjMuMCBBZGQgZGF0YXBvaW50cyBhcyBhIGJlZXN3YXJtDQogICAgLi4uDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA1LjMuMSBgZ2VvbV9xdWFzaXJhbmRvbSgpYCBhZGRzIEtERSBzdHJ1Y3R1cmUgdG8geW91ciBwbG90dGluZw0KDQpBcyB3ZSBjYW4gc2VlIGZyb20gYWJvdmUsIHRoZSBgZ2VvbV9iZWVzd2FybSgpYCBsYXllciBhZGRzIGEgbGl0dGxlIG1vcmUgc3RydWN0dXJlIHRvIHRoZSBkYXRhIGluIGEgc29tZXdoYXQgcmlnaWQgd2F5LiBBbnkgZGF0YXBvaW50cyB0aGF0IGFyZSBuZWFyIGVhY2ggb3RoZXIgYXJlIGRlbGliZXJhdGVseSBzcGFjZWQgb3V0IHRvIGFsbW9zdCByZXByZXNlbnQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB5b3VyIGRhdGEuIE9mIGNvdXJzZSwgeW91IG1heSBydW4gaW50byBzb21lIGlzc3VlcyBhcyB5b3VyIG51bWJlciBvZiBkYXRhcG9pbnRzIGluY3JlYXNlcyBvciBhcyB5b3VyIGRhdGEgcmFuZ2UgaW5jcmVhc2VzIChzZWUgdGhlIDIwIHRvIDM5IGFnZSBncm91cCkuDQoNClRvIHJlbWVkeSB0aGlzLCB3ZSBjYW4gYmFsYW5jZSB0aGUgdmlzdWFsaXphdGlvbiBhIGJpdCB3aXRoIHRoZSBgZ2VvbV9xdWFzaXJhbmRvbSgpYCBmdW5jdGlvbi4gYGdlb21fcXVhc2lyYW5kb20oKWAgd29ya3Mgc2ltaWxhcmx5IHRvIHRoZSBgZ2VvbV9iZWVzd2FybSgpYCBmdW5jdGlvbiB3aXRoIGVtcGhhc2lzIG9uIGFuIGFkZGl0aW9uYWwgbWV0aG9kIG9mIGhvdyB0aGUgcG9pbnRzIGFyZSBwbG90dGVkOg0KDQotICAgYG1ldGhvZGA6IGRldGVybWluZSB0aGUgbWV0aG9kIGZvciBkaXN0cmlidXRpbmcgdGhlIHBvaW50cy4gT3B0aW9ucyBpbmNsdWRlOg0KICAgIC0gICBxdWFzaXJhbmRvbSwgcHNldWRvcmFuZG9tOiBnZW5lcmF0ZXMgYSBLREUgYmVmb3JlIHBsb3R0aW5nIHBvaW50cyBpbiBhIHZpb2xpbiBzaGFwZQ0KICAgIC0gICBzbWlsZXksIGZyb3duZXk6IGdlbmVyYXRlcyBhIEtERSwgdGhlbiBwb2ludHMgYXJlIGJpbm5lZCB3aXRoIG1heGltdW0gYmluIHZhbHVlcyBwbG90dGVkIG9uIHRoZSBvdXRzaWRlIG9yIGluc2lkZSByZXNwZWN0aXZlbHkNCiAgICAtICAgdHVrZXk6IHBsb3R0ZWQgbW9yZSBhcyBkb3RzdHJpcHMgaW4gYSBib3gtcGxvdCBzdHlsZS4NCi0gICBgdmFyd2lkdGhgOiBpZiBUUlVFLCB2YXJ5IHRoZSB3aWR0aCBvZiBlYWNoIGdyb3VwIGJhc2VkIG9uIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyBpbiB0aGUgZ3JvdXANCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBHZW5lcmF0ZSBhIGJhc2ljIGJveCBwbG90IHdpdGggb3V0bGllcnMgcHJlc2VudA0KY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCiAgIyBGaWx0ZXIgZm9yIGN1bXVsYXRpdmUgZGF0YQ0KICBmaWx0ZXIocGVyaW9kID09ICJjdW11bGF0aXZlIikgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHg9YWdlX2dyb3VwLCB5ID0gcGVyY2VudF9jYXNlcykgKw0KDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArICMgc2V0IHRleHQgc2l6ZQ0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlPU5BLCAgIyBSZW1vdmUgb3V0bGllcnMgDQogICAgICAgICAgICAgICAgIG5vdGNoID0gVFJVRSkgKyAgICAjIEFkZCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgbm90Y2gNCg0KICAgICMjIyA1LjMuMSBBZGQgZGF0YXBvaW50cyBhcyBhIHF1YXNpcmFuZG9tIGJlZXN3YXJtDQogICAgLi4uDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCk5vdyBvdXIgZGF0YSBwb2ludHMgdGFrZSBhIG1vcmUgbnVhbmNlZCBhcHByb2FjaCB3aXRoIGEgdW5pZm9ybSB3aWR0aCB0aGF0IHNoYXBlcyB0aGUgZGF0YSBhcyBhIGRpc3RyaWJ1dGlvbi4gV2UnbGwgc2VlIHRoaXMgd2l0aCBldmVuIG1vcmUgZW1waGFzaXMgaW4gYSBmZXcgc2VjdGlvbnMuDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovYmVlc3dhcm1fam95LnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KDQpXaGVuIGdpdmVuIHRoZSBuZWVkIHRvIHNob3cgZGF0YSBkaXN0cmlidXRpb24sIHRyeSB0aGUgcXVhc2lyYW5kb20gcGxvdHRpbmcgb2YgcG9pbnRzIG92ZXIgYSBzaW1wbGUgYmVlc3dhcm0uDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDUuNC4wIEFkZCBhIHRoaXJkIGRpbWVuc2lvbiB0byB5b3VyIGJveHBsb3Qgd2l0aCBgZmlsbGANCg0KSXMgdGhlcmUgbW9yZSB0byB0aGUgZGF0YSB3ZSd2ZSB2aXN1YWxpemVkPyBXZSBjYW4gYWRkIGEgdGhpcmQgZGltZW5zaW9uIGluIGEgbnVtYmVyIG9mIHdheXMgYnV0IHRoZSBzaW1wbGVzdCB3b3VsZCBiZSB0byBjb21wYXJlIHRoZSBwcm9wb3J0aW9ucyBvZiB0b3RhbCBjYXNlcyB2cyB0b3RhbHMgaG9zcGl0YWxpemF0aW9ucyBvdmVyIHRoZSBjb3Vyc2Ugb2YgdGhpcyBwYW5kZW1pYy4gVG8gZG8gc28sIHdlIGNhbiBwaXZvdCBvdXIgZGF0YXNldCB0byBjb2xsYXBzZSBgcGVyY2VudF9jYXNlc2AgYW5kIGBwZXJjZW50X2hvc3BpdGFsaXphdGlvbnNgIHRvZ2V0aGVyIGludG8gYSBzaW5nbGUgdmFyaWFibGUuDQoNCkZyb20gdGhlcmUgd2UnbGwgdXNlIHRoZSBgZmlsbGAgcGFyYW1ldGVyIHRvIGdlbmVyYXRlIGRpZmZlcmVudCBzdWJncm91cHMgaW4gb3VyIGJveHBsb3QgdG8gbWFrZSBhIGdyb3VwZWQgYm94cGxvdC4gSW4gZG9pbmcgc28sIHdlJ2xsIGFsc28gaGF2ZSB0byBhZGQgdGhlIHVzZSBvZiB0aGUgYGdlb21fcXVhc2lyYW5kb20oKWAgcGFyYW1ldGVyczoNCg0KLSAgIGBkb2RnZS53aWR0aGA6IHNlcGFyYXRlIHRoZSBkYXRhIHBvaW50cyBieSBhbnkgYWVzdGhldGljIGdyb3VwcyB0aGF0IGhhdmUgYmVlbiBhc3NpZ25lZC4NCg0KLSAgIGB3aWR0aGA6IHNldCB0aGUgbWF4aW1pdW0gc3ByZWFkIG9mIHlvdXIgZGF0YSBwb2ludHMgd2l0aGluIGVhY2ggZ3JvdXBpbmcNCg0KLSAgIGBhbHBoYWA6IHNldCB0aGUgb3BhY2l0eSBvZiB5b3VyIGRhdGEgcG9pbnRzIHNvIHlvdSBjYW4gc2VlIG1vcmUgb2YgdGhlIG92ZXJsYXBwaW5nIGRhdGEuDQoNCkxhc3RseSwgd2UnbGwgZmFjZXQgdGhlIHBsb3QgYnkgcGVyaW9kIHNvIHdlIGNhbiwgeWV0IGFnYWluLCBjb21wYXJlIHRoZSBjdW11bGF0aXZlIGRhdGEgdnMgYSBtb3JlIHJlY2VudCBzbmFwc2hvdCBvZiB0aGUgZGF0YS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xNH0NCg0KY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCiAgIyBTZWxlY3QgZm9yIGp1c3QgdGhlIGltcG9ydGFudCBjb2x1bW5zIGluIG91ciBhbmFseXNpcw0KICBzZWxlY3QocGVyaW9kLCBwdWJsaWNfaGVhbHRoX3VuaXQsIGFnZV9ncm91cCwgcGVyY2VudF9jYXNlcywgcGVyY2VudF9ob3NwaXRhbGl6YXRpb25zKSAlPiUgDQoNCiAgIyBQaXZvdCB0aGUgbW9kaWZpZWQgdGFibGUgdG8gY2FwdHVyZSB0aGUgInN0YXRfZ3JvdXAiIG9mIHBlcmNlbnRfY2FzZXMgdnMgcGVyY2VudF9ob3NwaXRhbGl6YXRpb25zDQogIHBpdm90X2xvbmdlcihjb2xzPWMoNDo1KSwgbmFtZXNfdG8gPSAic3RhdF9ncm91cCIsIHZhbHVlc190byA9ICJwZXJjZW50X1BIVV90b3RhbCIpICU+JSANCiAgDQogICMgUGxvdCB0aGUgZGF0YSBhcyBhIGdyb3VwZWQgYm94cGxvdA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHg9YWdlX2dyb3VwLCB5ID0gcGVyY2VudF9QSFVfdG90YWwsIA0KICAgICAgICBmaWxsID0gLi4uKSArICMjIyA1LjQuMCBBbHRlciBvdXIgZmlsbCBhZXN0aGV0aWMNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZT1OQSwgbm90Y2ggPSBUUlVFKSArICMgQWRkIHRoZSBib3hwbG90IGdlb20NCiAgICAjIEFkZCBhIHF1YXNpcmFuZG9tIGRpc3RyaWJ1dGlvbiBvZiBvdXIgZGF0YSBwb2ludHMNCiAgICBnZW9tX3F1YXNpcmFuZG9tKGRvZGdlLndpZHRoID0gLi4uLCB3aWR0aCA9IC4uLiwgYWxwaGEgPSAwLjUpICsNCg0KICAgICMgNi4gRmFjZXQNCiAgICAjIyMgNS40LjAgUHJvZHVjZSBvdXIgZGF0YSBiYXNlZCBvbiBwZXJpb2QNCiAgICBmYWNldF93cmFwKH5wZXJpb2QsIG5yb3cgPSAyLCBzY2FsZXMgPSAiZnJlZV95IikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KSW4gb3VyIGJveHBsb3RzLCB3ZSBhcmUgcGxvdHRpbmcgdGhlIGRhdGEgaW4gYm90aCBhIGJveHBsb3QgYW5kIGRvdHBsb3QgZm9ybWF0LiBUaGUgc2hhcGUgb2YgdGhlIGRvdHMgaGVscGVkIHRvIGdpdmUgYSBiZXR0ZXIgc2Vuc2Ugb2YgUEhVIGRpc3RyaWJ1dGlvbiB3aXRoaW4gZWFjaCBhZ2UgZ3JvdXAuIFlvdSBjYW4gc2VlIHRoYXQgdGhlIGRhdGEgcG9pbnRzIG92ZXJsYXkgb24gdGhlIGJveCBidXQgYWxzbyBmYWxsIG91dHNpZGUuIENhbiB3ZSBnZXQgdGhlIGNvbXBhY3QgbmF0dXJlIG9mIHRoZSBib3hwbG90IHdoaWxlIHN0aWxsIGdldHRpbmcgdGhlIHZpc3VhbCBhcHBlYWwgZ2VuZXJhdGVkIGJ5IGEgZGVuc2l0eSBwbG90Pw0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNS41LjAgVmlvbGluIHBsb3QgLSB0aGUgbG92ZWNoaWxkIG9mIGRlbnNpdHkgYW5kIGJveHBsb3RzDQoNCkFzIHRoZSB0aXRsZSBzYXlzLCB0aGUgdmlvbGluIHBsb3QgaXMgYSBtaXh0dXJlIG9mIGJvdGggdGhlIGJveHBsb3QgYW5kIGtlcm5lbCBkZW5zaXR5IHBsb3QuIEl0J3MgYSBtaW5pYXR1cml6ZWQgS0RFIHRoYXQgaXMgbWlycm9yZWQgYWNyb3NzIGl0cyBheGlzLiBJdCBlbmNvbXBhc3NlcyB0aGUgc3VtbWFyeSBpbmZvcm1hdGlvbiBvZiB0aGUgYm94cGxvdCBidXQgaW4gYSBwZWFyIG9yIHZpb2xpbi1zaGFwZWQgZGlzdHJpYnV0aW9uLiBUbyBnZW5lcmF0ZSBhIGBnZW9tX3Zpb2xpbigpYCBpbiBgZ2dwbG90YCwgYSBtaW5pbXVtIG9mIHRocmVlIHZhbHVlcyBhcmUgcmVxdWlyZWQuIFRvIGp1c3RpZnkgdXNpbmcgYSB2aW9saW4sIEkgd291bGQgYWdhaW4gc3VnZ2VzdCBzdGlja2luZyB0byBhIHNpbWlsYXIgcnVsZSBvZiB0aHVtYiBvZiBhIG1pbmltdW0gXH4zMCBzYW1wbGVzL29ic2VydmF0aW9ucyB0byBlbnN1cmUgYW4gYWNjdXJhdGUgcmVwcmVzZW50YXRpb24gb2YgdGhlIGRpc3RyaWJ1dGlvbi4NCg0KVGhlIG51YW5jZWQgdmlzdWFsaXphdGlvbiBvZiBhIHZpb2xpbiBwbG90IGdpdmVzIG11Y2ggbW9yZSBpbmZvcm1hdGlvbiB0aGFuIHRoZSBib3ggcGxvdCBpdHNlbGYgYW5kIG1vc3QgYm94cGxvdHMgY2FuIGJlIHJlcGxhY2VkIHdpdGggYSB2aW9saW4gcGxvdC4gRGVzcGl0ZSB0aGUgZ2F0ZXdheSB0byBtb3JlIGRldGFpbGVkIGRpc3RyaWJ1dGlvbiBpbmZvcm1hdGlvbiwgdGhpcyBmb3JtYXQgcmVtYWlucyBsZXNzIHBvcHVsYXIvZmFtaWxpYXIgdG8gc2NpZW50aXN0cy4gVGhlcmVmb3JlIGl0cyBpbW1lZGlhdGUgYWNjZXNzaWJpbGl0eSB0byB5b3VyIGF1ZGllbmNlIGNhbiBiZSBsaW1pdGVkLg0KDQpBbiBpbXBvcnRhbnQgcGFyYW1ldGVyIG9mIHRoaXMgZ2VvbSBpcyBgc2NhbGVgOiB0aGlzIHNldHMgaG93IGJpZyBlYWNoIHZpb2xpbiBpcyBpbiBjb21wYXJpc29uIHRvIGVhY2ggb3RoZXIuIEl0IGFjY2VwdHMgdGhlIGZvbGxvd2luZyB2YWx1ZXM6DQoNCi0gICBgYXJlYWA6IGRlZmF1bHQgdmFsdWUgc28gYWxsIHZpb2xpbnMgd2lsbCBoYXZlIHRoZSBzYW1lIGFyZWEuDQoNCi0gICBgY291bnRgOiBzY2FsZSBhcmVhcyBwcm9wb3J0aW9uYXRlbHkgdG8gb2JzZXJ2YXRpb25zLg0KDQotICAgYHdpZHRoYDogYWxsIHZpb2xpbnMgaGF2ZSB0aGUgc2FtZSBtYXhpbXVtIHdpZHRoLiBUb3RhbCBhcmVhIGlzIG5vdCB0aGUgc2FtZSBmb3IgZWFjaC4NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC13YXJuaW5nfQ0KKipPdXRsaWVycyBhbmQgdmlvbGluczoqKiBNdWNoIGxpa2UgYSBLREUsIHRoZSB0aGVvcmV0aWNhbCBkaXN0cmlidXRpb24gb2YgYSB2aW9saW4gcGxvdCBjYW4gZ2VuZXJhdGUgc29tZSBpbXBvc3NpYmxlIHZhbHVlcyAtIGVzcGVjaWFsbHkgYXQgdGhlIHRhaWxzLiBSZW1lbWJlciB0aGF0IHRoaXMgaXMgYSB0aGVvcmV0aWNhbCBkaXN0cmlidXRpb24gYmFzZWQgb24gdGhlIGRhdGEgc3VwcGxpZWQuIERlcGVuZGluZyBvbiBob3cgbXVjaCB2YXJpYXRpb24gaXMgaW4geW91ciBkYXRhLCBhbmQgaG93IG1hbnkgb3V0bGllcnMgaXQgaGFzLCBpdCBjYW4gcmVhbGx5IGFmZmVjdCB0aGUgc2hhcGUgb2YgeW91ciB2aW9saW4uDQo6OjoNCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCiAgIyBGaWx0ZXIgZm9yIG9ubHkgY3VtdWxhdGl2ZSBkYXRhDQogIGZpbHRlcihwZXJpb2QgPT0gImN1bXVsYXRpdmUiKSAlPiUgDQogIA0KICAjIFNlbGVjdCBmb3IganVzdCB0aGUgaW1wb3J0YW50IGNvbHVtbnMgaW4gb3VyIGFuYWx5c2lzDQogIHNlbGVjdChwZXJpb2QsIHB1YmxpY19oZWFsdGhfdW5pdCwgYWdlX2dyb3VwLCBwZXJjZW50X2Nhc2VzLCBwZXJjZW50X2hvc3BpdGFsaXphdGlvbnMpICU+JSANCg0KICAjIFBpdm90IHRoZSBtb2RpZmllZCB0YWJsZSB0byBjYXB0dXJlIHRoZSAic3RhdF9ncm91cCIgb2YgcGVyY2VudF9jYXNlcyB2cyBwZXJjZW50X2hvc3BpdGFsaXphdGlvbnMNCiAgcGl2b3RfbG9uZ2VyKGNvbHM9Yyg0OjUpLCBuYW1lc190byA9ICJzdGF0X2dyb3VwIiwgdmFsdWVzX3RvID0gInBlcmNlbnRfUEhVX3RvdGFsIikgJT4lIA0KICANCiAgIyBQbG90IHRoZSBkYXRhIGFzIGEgZ3JvdXBlZCBib3hwbG90DQogIA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeD1hZ2VfZ3JvdXAsIHkgPSBwZXJjZW50X1BIVV90b3RhbCwgZmlsbCA9IHN0YXRfZ3JvdXApICsNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDAuNikpICsgIyBTZXQgdGhlIHktYXhpcyBsaW1pdA0KDQogICAgIyA0LiBHZW9tcw0KICAgIC4uLiArICMjIEFkZCB0aGUgdmlvbGluIGdlb20NCg0KICAgICMgQWRkIGEgcXVhc2lyYW5kb20gZGlzdHJpYnV0aW9uIG9mIG91ciBkYXRhIHBvaW50cw0KICAgIGdlb21fcXVhc2lyYW5kb20oZG9kZ2Uud2lkdGggPSAwLjg1KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA1LjYuMCBWaW9saW4gcGxvdHMgcmVwcmVzZW50IGRpc3RyaWJ1dGlvbnMgYW5kIGJveHBsb3RzIHN1bW1hcml6ZSB0aGVtDQoNClRoZSBtYWpvciBhZHZhbnRhZ2UgdG8gdGhlIHZpb2xpbiBwbG90IGlzIHRoYXQsIGJ5IGl0J3MgbmF0dXJlLCBpdCBpcyB2ZXJ5IHNlbnNpdGl2ZSB0byB0aGUgZGlzdHJpYnV0aW9uIHRoYXQgcHJvZHVjZXMgdGhlIGRlbnNpdHkgZXN0aW1hdGUuIFRoZSBib3hwbG90IHJlcHJlc2VudHMgdGhlIHN1bW1hcnkgaW5mb3JtYXRpb24gb2YgYSBkaXN0cmlidXRpb24gYnV0IGlzICphbHdheXMqIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIGEgKm5vcm1hbCogZGlzdHJpYnV0aW9uLiBUaGVyZSBhcmUgbm90IGVub3VnaCBwYXJhbWV0ZXJzIHN1cHBsaWVkIHRvIHJlcHJlc2VudCBhbnl0aGluZyBtb3JlIGNvbXBsZXghDQoNClRoZSB2aW9saW4gcGxvdCBpcyBub3QgbGltaXRlZCBpbiB0aGF0IHJlc3BlY3QuIERlc3BpdGUgc29tZSBvZiBpdCdzIHZpc3VhbCBjYXZlYXRzLCBpdCBjYW4gY2VydGFpbmx5IGRldGVjdCAqKiptdWx0aS1tb2RhbCoqKiBkYXRhLiBMZXQncyBtYWtlIGEgdG95IGV4YW1wbGUgdG8gaWxsdXN0cmF0ZS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCiAgIyBGaWx0ZXIgZm9yIG9ubHkgY3VtdWxhdGl2ZSBkYXRhDQogIGZpbHRlcihwZXJpb2QgPT0gImN1bXVsYXRpdmUiKSAlPiUgDQoNCiAgIyBTZWxlY3QgZm9yIGp1c3QgdGhlIGltcG9ydGFudCBjb2x1bW5zIGluIG91ciBhbmFseXNpcw0KICBzZWxlY3QocGVyaW9kLCBwdWJsaWNfaGVhbHRoX3VuaXQsIGFnZV9ncm91cCwgcGVyY2VudF9jYXNlcywgcGVyY2VudF9ob3NwaXRhbGl6YXRpb25zKSAlPiUgDQogIA0KICAjIFBpdm90IHRoZSBtb2RpZmllZCB0YWJsZSB0byBjYXB0dXJlIHRoZSAic3RhdF9ncm91cCIgb2YgcGVyY2VudF9jYXNlcyB2cyBwZXJjZW50X2hvc3BpdGFsaXphdGlvbnMNCiAgcGl2b3RfbG9uZ2VyKGNvbHM9Yyg0OjUpLCBuYW1lc190byA9ICJzdGF0X2dyb3VwIiwgdmFsdWVzX3RvID0gInBlcmNlbnRfUEhVX3RvdGFsIikgJT4lICANCiAgDQogICMgUGxvdCBhIGNvbWJpbmVkIHZpb2xpbiBhbmQgYm94cGxvdCB0byBzaG93IGRpZmZlcmVuY2UgaW4gZGlzdHJpYnV0aW9ucw0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHggPSBwZXJjZW50X1BIVV90b3RhbCwgeSA9IGFnZV9ncm91cCkgKyAgIyMjIDUuNi4wIFN3YXAgdGhlIHggYW5kIHkgYXhlcw0KICAgIHRoZW1lX2J3KCkrDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArICMgc2V0IHRleHQgc2l6ZQ0KDQogICAgIyA0LiBHZW9tcw0KICAgIC4uLihzY2FsZSA9ICJ3aWR0aCIsIGNvbG91cj0iZGFya3Zpb2xldCIpICsgDQogICAgZ2VvbV9ib3hwbG90KGFscGhhID0gMC42KSArIA0KICAgIGdlb21fcXVhc2lyYW5kb20oYWVzKGNvbG91ciA9IC4uLikpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDUuNy4wIENvbWJpbmUgdmlvbGluIGFuZCBib3hwbG90cyBpbnRvIHRoZSB1bHRpbWF0ZSBwbG90DQoNCkZyb20gb3VyIGFib3ZlIGV4YW1wbGUsIHlvdSBjYW4gc2VlIHRoYXQgd2UgYmxlbmRlZCBhIG51bWJlciBvZiBgZ2VvbXNgIHRvZ2V0aGVyLiBXaXRoIGEgbGl0dGxlIHdvcmtpbmcgYXJvdW5kLCB3ZSBjYW4gYWxzbyBwbG90IGJvdGggdmlvbGlucyBhbmQgYm94cGxvdHMgdG9nZXRoZXIgaW4gYSBtdWx0aXZhcmlhdGUgc2V0dGluZyEgVGhpcyBnaXZlcyB1cyB0aGUgZmFtaWxpYXJpdHkgb2YgdGhlIGJveHBsb3QgYnV0IGFsc28gY2xlYXJseSBkaXNwbGF5cyB0aGUgdGhlb3JldGljYWwgZGlzdHJpYnV0aW9uLiBTb21lIHN0ZXBzIHRvIGFjY29tcGxpc2ggdGhpczoNCg0KMS4gIFRvIHB1dCBlbXBoYXNpcyBvbiB0aGUgdmlvbGluIHBsb3RzIHdlIHNldCB0aGUgYHNjYWxlYCBwYXJhbWF0ZXIgdG8gIndpZHRoIi4NCjIuICBXZSBuZWVkIHRvIGFkanVzdCBzb21lIG9mIHRoZSBib3hwbG90IHBhcmFtZXRlcnMgdG8gZml0IHRoZW0gd2l0aGluIHRoZSB2aW9saW4gcGxvdHMNCjMuICBTb21lIGFkanVzdG1lbnRzIHRvIHRoZSBgYWVzKClgIHBhcmFtZXRlcnMgZm9yIG91ciBnZW9tcyB0byBlbnN1cmUgb3VyIHBvaW50cyBhcmUgcGxvdHRlZCBjb3JyZWN0bHkuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCmNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiAlPiUgDQogICMgRmlsdGVyIGZvciBvbmx5IGN1bXVsYXRpdmUgZGF0YQ0KICBmaWx0ZXIocGVyaW9kID09ICJjdW11bGF0aXZlIikgJT4lIA0KICANCiAgIyBTZWxlY3QgZm9yIGp1c3QgdGhlIGltcG9ydGFudCBjb2x1bW5zIGluIG91ciBhbmFseXNpcw0KICBzZWxlY3QocGVyaW9kLCBwdWJsaWNfaGVhbHRoX3VuaXQsIGFnZV9ncm91cCwgcGVyY2VudF9jYXNlcywgcGVyY2VudF9ob3NwaXRhbGl6YXRpb25zKSAlPiUgDQogIA0KICAjIFBpdm90IHRoZSBtb2RpZmllZCB0YWJsZSB0byBjYXB0dXJlIHRoZSAic3RhdF9ncm91cCIgb2YgcGVyY2VudF9jYXNlcyB2cyBwZXJjZW50X2hvc3BpdGFsaXphdGlvbnMNCiAgcGl2b3RfbG9uZ2VyKGNvbHM9Yyg0OjUpLCBuYW1lc190byA9ICJzdGF0X2dyb3VwIiwgdmFsdWVzX3RvID0gInBlcmNlbnRfUEhVX3RvdGFsIikgJT4lICANCiAgDQogICMgUGxvdCB0aGUgZGF0YSBhcyBhIGdyb3VwZWQgYm94cGxvdA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHg9YWdlX2dyb3VwLCB5ID0gcGVyY2VudF9QSFVfdG90YWwpICsNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwgImJsYWNrIikpKyAjIHdlJ2xsIG5lZWQgdGhpcyB0byBmaXggb3VyIGJveHBsb3RzDQoNCiAgICAjIDQuIERhdGENCiAgICAjIG11bHRpLWZhY3RvciB2aW9saW4gcGxvdHMgYnV0IGtlZXAgdGhlIHdpZHRoIGNvbnNpc3RlbnQNCiAgICBnZW9tX3Zpb2xpbihzY2FsZT0id2lkdGgiLCBhZXMoZmlsbD0uLi4pKSArIA0KDQogICAgIyBCb3hwbG90IGJ1dCBzbWFsbGVyIHdpZHRoIHNvIHRoZXkgcmVzaWRlICJ3aXRoaW4iIHRoZSB2aW9saW4gcGxvdA0KICAgIGdlb21fYm94cGxvdChhZXMoY29sb3VyID0gLi4uKSwgd2lkdGg9MC4yLCANCiAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSAuLi4sIA0KICAgICAgICAgICAgICAgICBvdXRsaWVyLnNoYXBlPU5BKSArICMgUmVtb3ZlIHRoZSBvdXRsaWVycw0KDQogICAgIyMjIDUuNy4wIEFkZCBpbiBhbGwgb2YgdGhlIGRhdGEgcG9pbnRzDQogICAgZ2VvbV9xdWFzaXJhbmRvbShkb2RnZS53aWR0aCA9IDAuODUsIGFlcyhncm91cD1zdGF0X2dyb3VwKSwgYWxwaGEgPSAwLjgpDQpgYGANCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1kYW5nZXJ9DQoqKlNlY3Rpb24gNS43LjAgQ29tcHJlaGVuc2lvbiBRdWVzdGlvbjoqKiBMb29raW5nIGF0IHRoZSBhYm92ZSBjb2RlIGZvciBvdXIgZ3JhcGgsIHdoeSBkbyB5b3UgdGhpbmsgd2Ugc2V0IHRoZSAqKmFlcyhmaWxsL2dyb3VwID0gc3RhdF9ncm91cCkqKiBhZXN0aGV0aWMgaW5kaXZpZHVhbGx5IGZvciBzb21lIGxheWVycyByYXRoZXIgdGhhbiBkaXJlY3RseSBpbiB0aGUgKiphZXMoKSoqIGxheWVyPw0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA1LjguMCBQYXJhbGxlbCBjb29yZGluYXRlIHBsb3RzIGNhbiBoZWxwIHZpc3VhbGl6ZSBtdWx0aXZhcmlhdGUgZGF0ZQ0KDQpXaGlsZSB0aGUgYWJvdmUgdmlzdWFsaXphdGlvbiBzaG93cyB3aXRoIHNvbWUgY2xhcml0eSB0aGUgdG90YWwgZGlzdHJpYnV0aW9uIG9mIG91ciBhZ2UgZ3JvdXBzLCB0aGUgbWVzc2luZXNzIG9mIG91dGxpZXJzIGhhcyBpbnZhZGVkIGludG8gc29tZSBvZiB0aGUgdmlvbGluIHBsb3RzIHRoZW1zZWx2ZXMuIEFuIGV4cGVyaWVuY2VkIGV5ZSwgaG93ZXZlciBjYW4gc3RpbGwgc2VlIHRoYXQgdGhlIGJ1bGsgb2YgdGhlIHBvcHVsYXRpb24gY2VudHJlcyBhcm91bmQgb3VyIGludGVybmFsIGJveHBsb3RzIG9mZnNldCBieSB0aGUgc3RyZXRjaGVkIG91dCBsb29rIG9mIG91ciB2aW9saW5zLiBXZSBjb3VsZCwgb2YgY291cnNlIGNsZWFuIGl0IHVwIGJ5IHJlbW92aW5nIHNvbWUgb2YgdGhlIHNtYWxsZXIgUEhVIHBvcHVsYXRpb25zIG9yIHJlbW92aW5nIG91dGxpZXJzIGFoZWFkIG9mIHRpbWUuDQoNClN1cHBvc2UsIGhvd2V2ZXIsIHdlIHdhbnRlZCB0byBhZGQgbW9yZSBsZXZlbHMgdG8gb3VyIGRhdGEgbGlrZSBgcGVyY2VudF9tYWxlX2Nhc2VzYCBhbmQgYHBlcmNlbnRfZmVtYWxlX2Nhc2VzYD8gV2l0aCA3IGFnZSBncm91cHMgcmVwcmVzZW50ZWQsIHRoaW5ncyB3b3VsZCBzdGFydCB0byBnZXQgdmVyeSBjcm93ZGVkLiBUbyBhY2NvbW1vZGF0ZSBhbGwgb2YgdGhhdCBkYXRhLCB5b3UgY291bGQgZmFjZXQgaXQgaW50byA0IGdyb3VwcyBiYXNlZCBvbiB0aGUgc3RhdGlzdGljIHVzZWQgYnV0IHRoaXMgd291bGQgc2VwYXJhdGUgdGhlIGRhdGEsIHdoaWNoIGxvb2tzIHNoYXJwZXIgd2hlbiB5b3UgY2FuIHNlZSBpdCBhbGwgb24gYSBzaW5nbGUgcGxvdC4NCg0KT3JnYW5pemluZyBtdWx0aXBsZSBncm91cHMsIGFjcm9zcyBtdWx0aXBsZSBjYXRlZ29yaWVzIGlzIHRoZSBkb21haW4gb2YgdGhlIHBhcmFsbGVsIGNvb3JkaW5hdGUgcGxvdC4gVGhlIGBHR2FsbHlgIHBhY2thZ2UgaXMgYSBgZ2dwbG90MmAgZXh0ZW5zaW9uIHdpdGggdGhlIGBnZ3BhcmNvb3JkYCBmdW5jdGlvbiB3aGljaCBhbGxvd3MgdXMgdG8gbG9vayBzaW11bHRhbmVvdXNseSBhdCBvdXIgMyBpbmRpY2F0b3JzICh0b3RhbCBjYXNlcywgZGVhdGhzLCBhbmQgaG9zcGl0YWxpemF0aW9ucykgZm9yIGVhY2ggYWdlIGdyb3VwLCBsaW5raW5nIGVhY2ggUEhVLiBGb3IgZWFjaCBQSFUgYXQgZWFjaCBpbmRpY2F0b3IsIHdlIHdpbGwgZHJhdyBhIGxpbmUgY29ubmVjdGluZyBhbGwgdmFsdWVzIGFjcm9zcyB0aGUgYWdlIGdyb3VwcyAoY29vcmRpbmF0ZXMpLiBXZSB3aWxsIGNvbG91ciBvdXIgbGluZXMgYmFzZWQgb24gdGhlIGNhdGVnb3J5IG9mIHRoZSBpbmRpY2F0b3IuDQoNCkFsdGhvdWdoIGBHR2FsbHlgIGlzIGFuIGV4dGVuc2lvbiBmb3IgYGdncGxvdDJgLCB3ZSBhY3R1YWxseSBoYXZlIHRvIHdyZXN0bGUgd2l0aCBvdXIgZGF0YSBhIGxpdHRsZSBiaXQgbW9yZSBhbmQgcHV0IGl0IGJhY2sgaW50byBhIHdpZGVyIGZvcm1hdC4gT3RoZXJ3aXNlIHdlIGNhbiB0cmVhdCB0aGUgcGxvdCBzaW1pbGFybHkgYWZ0ZXIgbWFraW5nIGl0IGJ5IGNoYW5naW5nIHRoZW1lcyBldGMuDQoNClRoZSBwYXJhbWV0ZXJzIGZvciBgZ2dwYXJjb29yZCgpYCByZXF1aXJlOg0KDQotICAgYGNvbHVtbnNgOiBhIG51bWVyaWMgdmVjdG9yIG9mIHRoZSBsb2NhdGlvbiBvZiBjb2x1bW5zIHRoYXQgd2lsbCByZXByZXNlbnQgdGhlIGNvb3JkaW5hdGVzIChjYXRlZ29yaWVzL2dyb3Vwcy92YXJpYWJsZXMpLg0KDQotICAgYGdyb3VwQ29sdW1tYDogdGhlIGNvbHVtbiB0aGF0IGRlZmluZXMgdGhlIGluZGljYXRvciB0eXBlIChgc3RhdF9ncm91cGApIGZvciB0aGUgb2JzZXJ2YXRpb25zLg0KDQotICAgYHNjYWxlYDogZWFjaCBjb29yZGluYXRlIHNob3VsZCB0aGVvcmV0aWNhbGx5IGhhdmUgaXQncyBvd24gc2NhbGUgYnV0IHRoYXQgaXNuJ3QgYWx3YXlzIHBvc3NpYmxlIGRlcGVuZGluZyBvbiB0aGUgZGF0YS4gSW5zdGVhZCwgdGhlIGRlZmF1bHQgbWV0aG9kIG9mIHNjYWxpbmcgaXMgdG8gbm9ybWFsaXplIHBvaW50cyBpbiB0ZXJtcyBvZiB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBkYXRhIGFsb25nIHRoYXQgY29vcmRpbmF0ZS4gWW91IGNhbiwgaG93ZXZlciwgY2hvb3NlIHRvIGp1c3QgdXNlIHRoZSBvcmlnaW5hbCBzY2FsZSB3aXRoIGBnbG9iYWxtaW5tYXhgIGlmIHRoZSByYW5nZXMgb2YgY29sdW1ucyBhcmVuJ3QgdG9vIGRpc3BhcmF0ZS4NCg0KVG8gZ2VuZXJhdGUgdGhpcyB2aXN1YWxpemF0aW9uLCB3ZSdsbCB3YW50IHRvOg0KDQoxXC4gUmVtYWtlIG91ciBsaXN0IG9mIFBIVXMgb3JkZXJlZCBieSBkZXNjZW5kaW5nIGNhc2UgbG9hZC4NCg0KMlwuIFBpdm90IG91ciBkYXRhIHRvIHdpZGUtZm9ybWF0LCBnaXZpbmcgZWFjaCBhZ2UgZ3JvdXAgaXQncyBvd24gY29sdW1uIGluIG91ciBkYXRhDQoNCmBgYHtyfQ0KIyBHZW5lcmF0ZSB0aGUgb3JkZXJlZCBsaXN0IG9mIFBIVXMgYnkgdG90YWwgY2FzZXMNCnBodV9ieV9jYXNlcyA8LSBwaHVfaW5mb3JtYXRpb24uZGYgJT4lIA0KICBzbGljZSgyOm4oKSkgJT4lICMgZHJvcCB0aGUgT250YXJpbyBkYXRhDQogIHNlbGVjdCgxLCAyKSAlPiUgIyBPbmx5IHRha2UgZ2VvZ3JhcGhpY19hcmVhIGFuZCBjYXNlX2NvdW50DQogIGFycmFuZ2UoZGVzYyhjYXNlX2NvdW50KSkgJT4lICMgc29ydCBieSBkZXNjZW5kaW5nIG9yZGVyDQogIHNlbGVjdCgxKSAlPiUgIyBncmFiIGp1c3QgdGhlIFBIVSBuYW1lcw0KICB1bmxpc3QoKSAlPiUgIyBVbmxpc3QNCiAgYXMuY2hhcmFjdGVyKCkgIyBDb252ZXJ0IHRvIGEgY2hhcmFjdGVyIHZlY3Rvcg0KDQojIExvb2sgYXQgdGhlIG9yZGVyZWQgbGlzdCBvZiBQSFVzDQpwaHVfYnlfY2FzZXMNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQpjb3ZpZF9wY3AuZGYgPC0NCg0KI2hlYWQoY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmKQ0KY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCg0KICAjIEZpbHRlciBmb3IgdGhlIHRvcCAxNSBQSFVzIGJ5IGNhc2UgbG9hZA0KICBmaWx0ZXIocHVibGljX2hlYWx0aF91bml0ICVpbiUgcGh1X2J5X2Nhc2VzWzE6MTVdLCANCiAgICAgICAgIHBlcmlvZCA9PSAiY3VtdWxhdGl2ZSINCiAgICAgICAgKSAlPiUgDQogIA0KICAjIFNlbGVjdCBmb3IganVzdCB0aGUgaW1wb3J0YW50IGNvbHVtbnMNCiAgc2VsZWN0KHBlcmlvZCwgcHVibGljX2hlYWx0aF91bml0LCBhZ2VfZ3JvdXAsIHBlcmNlbnRfY2FzZXMsIHBlcmNlbnRfaG9zcGl0YWxpemF0aW9ucywgDQogICAgICAgICBwZXJjZW50X21hbGVfY2FzZXMsIHBlcmNlbnRfZmVtYWxlX2Nhc2VzKSAlPiUgDQogIA0KICAjIFBpdm90IHRoZSBtb2RpZmllZCB0YWJsZSB0byBjYXB0dXJlIHRoZSAic3RhdF9ncm91cCIgb2YgcGVyY2VudF9jYXNlcw0KICAjIHBlcmNlbnRfaG9zcGl0YWxpemF0aW9ucywgYW5kIHBlcmNlbnRfeF9jYXNlcw0KICBwaXZvdF9sb25nZXIoY29scz1jKDQ6NyksIG5hbWVzX3RvID0gInN0YXRfZ3JvdXAiLCB2YWx1ZXNfdG8gPSAicGVyY2VudF9QSFVfdG90YWwiKSAlPiUgDQogIA0KICAjIFdlJ3JlIGdvaW5nIHRvIHJlb3JkZXIgdGhlIGZhY3RvciBhaGVhZCBvZiB0aW1lDQogICMgUHJvYmFibHkgYSBiZXR0ZXIgd2F5IHRvIGRvIHRoaXMgYnV0IHJlcXVpcmluZyBtb3JlIGNvZGUNCiAgbXV0YXRlKHN0YXRfZ3JvdXAgPSBmYWN0b3Ioc3RhdF9ncm91cCwgbGV2ZWxzPWMoInBlcmNlbnRfaG9zcGl0YWxpemF0aW9ucyIsICJwZXJjZW50X2Nhc2VzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwZXJjZW50X21hbGVfY2FzZXMiLCAicGVyY2VudF9mZW1hbGVfY2FzZXMiKSkpICU+JSANCiAgDQogICMgTm93IHdlJ3JlIGdvaW5nIHRvIHBpdm90IG91dCB3aWRlIHRvIGdpdmUgZWFjaCBhZ2VfZ3JvdXAgaXQncyBvd24gY29sdW1uDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBhZ2VfZ3JvdXAsIHZhbHVlc19mcm9tID0gYyhwZXJjZW50X1BIVV90b3RhbCkpICAlPiUgDQogIA0KICAjIEZpeCB0aGUgcG9zaXRpb25pbmcgb2Ygb3VyIGZhY3RvciBsZXZlbHMNCiAgcmVsb2NhdGUoJzUgdG8gMTEnLCAuYWZ0ZXIgPSAnMCB0byA0JykNCg0KaGVhZChjb3ZpZF9wY3AuZGYpDQoNCiMgR2VuZXJhdGUgYSBnZ3Bsb3Qgb2JqZWN0DQojIDEuIERhdGEgYW5kIEdlb21zDQpnZ3BhcmNvb3JkKC4uLiwgDQogICAgICAgICAgIGNvbHVtbnM9Li4uLCANCiAgICAgICAgICAgZ3JvdXBDb2x1bW49Li4uLA0KICAgICAgICAgICBzaG93UG9pbnRzID0gLi4uLCANCiAgICAgICAgICAgc2NhbGU9Li4uKSArDQoNCiAgIyAyLiBBZXN0aGV0aWNzDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpICsgIyBzZXQgdGV4dCBzaXplDQogIHhsYWIoIkFnZSBncm91cCIpICsNCiAgeWxhYigiUGVyY2VudCBvZiBjYXRlZ29yeSBieSBpbmRpdmlkdWFsIFBIVSIpICsgICANCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iQ2F0ZWdvcnkiKSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDUuOC4xIGBncm91cGAgeW91ciBkYXRhIGJ5IG11bHRpcGxlIHZhcmlhYmxlcyB3aXRoIHRoZSBgaW50ZXJhY3Rpb24oKWAgZnVuY3Rpb24NCg0KU29tZXRpbWVzIGluIHlvdXIgZGF0YSB5b3UgbWF5IHdhbnQgc3BlY2lmaWMgcGFydHMgb2YgeW91ciBkYXRhIHRvIGJlIGdyb3VwZWQgdG9nZXRoZXIsIGV2ZW4gaWYgeW91IGFyZW4ndCBwbGFubmluZyBvbiB1c2luZyB0aGF0IGluZm9ybWF0aW9uICpkaXJlY3RseSogdG8gZGV0ZXJtaW5lIGEgc3BlY2lmaWMgYWVzdGhldGljIGNhdGVnb3J5IGxpa2UgYHNpemVgLCBgY29sb3VyYCBvciBgZmlsbGAuDQoNCkZvciBleGFtcGxlLCBvdXIgcGFyYWxsZWwgY29vcmRpbmF0ZSBwbG90IGZyb20gYWJvdmUsICpjb3VsZCogYmUgcmVwcm9kdWNlZCBkaXJlY3RseSBpbiBnZ3Bsb3QuIEFzIGxvbmcgYXMgd2UgaGF2ZSBvdXIgZGF0YSBpbiB0aGUgcHJvcGVyIGxvbmcgZm9ybWF0LCB3ZSBjYW4gdXNlIHNwZWNpZmljIHZhcmlhYmxlcyB0byBlbnN1cmUgb3VyIGRhdGEgaXMgZGlzcGxheWVkIHByb3Blcmx5IGJ5IHVzaW5nIHRoZSBgZ3JvdXBgIGFlc3RoZXRpYy4NCg0KSW4gdGhlIGNhc2Ugb2Ygb3VyIGRhdGEsIHdlJ2QgbGlrZSB0byBiZSBzdXJlIHdlIGNhbiBncm91cCBvdXIgZGF0YSBsaW5lcyBieSBmb2xsb3dpbmcgdGhlaXIgYGFnZV9ncm91cGAgYW5kIGRhdGEgY2F0ZWdvcmllcyAoaWUgcGVyY2VudF9jYXNlcywgcGVyY2VudF9ob3NwaXRhbGl6YXRpb25zLCBldGMuKS4gSW4gb3VyIHdyYW5nbGVkIGRhdGFmcmFtZSB0aGlzIHdpbGwgZmFsbCB1bmRlciB0aGUgYHN0YXRfZ3JvdXBgIHZhcmlhYmxlIGFmdGVyIHBpdm90aW5nIGxvbmdlci4NCg0KVGhlIHByb2JsZW0gd2l0aCB0aGlzIHN0cmF0ZWd5LCBob3dldmVyLCBpcyB0aGF0IGBncm91cGAgZG9lcyBub3Qgbm9ybWFsbHkgYWxsb3cgZm9yIG1vcmUgdGhhbiBvbmUgdmFyaWFibGUgLSBsaWtlIG1vc3QgYWVzdGhldGljcy4gV2UgY2FuIHNvbHZlIHRoaXMgd2l0aCB0aGUgYGludGVyYWN0aW9uKClgIGZ1bmN0aW9uIHdoaWNoIHdpbGwgYWxsb3cgdXMgdG8gbmFtZSAqbXVsdGlwbGUqIGZhY3RvcnMgdGhhdCB3aWxsIGJlIHVzZWQgdG8gZm9ybWFsbHkgY2F0ZWdvcml6ZSBvYnNlcnZhdGlvbnMgYXMgYSBjb21wb3NpdGUgb2YgdGhvc2UgZmFjdG9ycy4NCg0KV2l0aCB0aGF0IGluIG1pbmQsIGxldCdzIG1vY2sgdXAgYSBxdWljayBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3Qgb2Ygb3VyIG93biENCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCiAgIyBGaWx0ZXIgZm9yIG9ubHkgY3VtdWxhdGl2ZSBkYXRhIGFuZCByZWR1Y2UgdGhlIFBIVSBkYXRhIHRvIDE1IGdyb3Vwcw0KICBmaWx0ZXIocHVibGljX2hlYWx0aF91bml0ICVpbiUgcGh1X2J5X2Nhc2VzWzE6MTVdLCANCiAgICAgICAgIHBlcmlvZCA9PSAiY3VtdWxhdGl2ZSINCiAgICAgICAgKSAlPiUgDQogIA0KICAjIFNlbGVjdCBmb3IganVzdCB0aGUgaW1wb3J0YW50IGNvbHVtbnMgaW4gb3VyIGFuYWx5c2lzDQogIHNlbGVjdChwZXJpb2QsIHB1YmxpY19oZWFsdGhfdW5pdCwgYWdlX2dyb3VwLCBwZXJjZW50X2Nhc2VzLCBwZXJjZW50X2hvc3BpdGFsaXphdGlvbnMsIA0KICAgICAgICAgcGVyY2VudF9tYWxlX2Nhc2VzLCBwZXJjZW50X2ZlbWFsZV9jYXNlcykgJT4lIA0KICANCiAgIyBQaXZvdCB0aGUgbW9kaWZpZWQgdGFibGUgdG8gY2FwdHVyZSB0aGUgInN0YXRfZ3JvdXAiIG9mIHBlcmNlbnRfY2FzZXMgdnMgcGVyY2VudF9ob3NwaXRhbGl6YXRpb25zDQogIHBpdm90X2xvbmdlcihjb2xzPWMoNDo3KSwgbmFtZXNfdG8gPSAic3RhdF9ncm91cCIsIHZhbHVlc190byA9ICJwZXJjZW50X1BIVV90b3RhbCIpICU+JSAgDQogIA0KICBnZ3Bsb3QoKSArIA0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4ID0gYWdlX2dyb3VwLCB5ID0gcGVyY2VudF9QSFVfdG90YWwpICsNCg0KICAgICMgU2V0IGEgYmFzaWMgdGhlbWUNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCg0KICAgICMgNC4gR2VvbXMNCiAgICAjIyMgNS44LjEgQSBsaW5lIHBsb3QgdG8gam9pbiB0aGUgcG9pbnRzDQogICAgZ2VvbV9saW5lKGFlcyhncm91cCA9IC4uLiwgDQogICAgICAgICAgICAgICAgICBjb2xvdXIgPSBzdGF0X2dyb3VwKSkgKw0KDQogICAgIyMjIDUuOC4xIEFkZCB0aGUgcG9pbnRzIGluIGFsc28gdG8gaGVscCBmb2xsb3cgdGhlIGNvbm5lY3Rpb25zDQogICAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gc3RhdF9ncm91cCkpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCkxvb2tzIG5lYXJseSB0aGUgc2FtZSBBTkQgeW91IGRvbid0IGhhdmUgdG8gcmVtZW1iZXIgaG93IHRvIHByb3Blcmx5IGZvcm1hdCB5b3VyIGRhdGEgZm9yIHVzaW5nIGBnZ3BhcmNvb3JkYCENCg0KIyMgNS45LjAgRXhwbG9yZSB0aGUgdHJlbmRzIHdpdGhpbiBncm91cHMgYWZ0ZXIgZXhwbG9yaW5nIGRhdGEgYWNyb3NzIGdyb3Vwcw0KDQpGcm9tIG91ciBhYm92ZSBkYXRhIHdlIHNlZSBzb21lIHZlcnkgaW50ZXJlc3RpbmcgcG9pbnRzIGxvb2tpbmcgYXQgb3VyIDYwLTc5IGFuZCA4MCsgY2F0ZWdvcmllcy4NCg0KMS4gIFRoZXNlIHR3byBncm91cHMgaGF2ZSB0aGUgaGlnaGVzdCBzaGFyZSBvZiBob3NwaXRhbGl6YXRpb25zIGFjcm9zcyBhbGwgYWdlIGdyb3Vwcy4NCjIuICBDb252ZXJzZWx5LCB0aGVzZSBncm91cHMgaGF2ZSBsb3dlciBzaGFyZXMgb2YgYWN0dWFsIFNBUlMtQ29WLTIgY2FzZXMuDQoNClJlbWVtYmVyIGFsbCBvZiB0aGlzIGRhdGEgcmVwcmVzZW50cyB0aGUgcHJvcG9ydGlvbiBvciBzaGFyZSBvZiBlYWNoIGFnZSBncm91cCBmb3IgZWFjaCBwYXJ0aWN1bGFyIGluZGljYXRvci4gSWYsIGhvd2V2ZXIsIHdlIHJlYWxseSB3YW50IHRvIHVuZGVyc3RhbmQgd2hpY2ggYWdlIGdyb3VwIGhhcyB0aGUgd29yc3QgaG9zcGl0YWxpemF0aW9uIG9yIGNhc2UgcmF0ZSwgd2UgbmVlZCB0byBkcmlsbCBkb3duIGludG8gZWFjaCBhZ2UgZ3JvdXAuDQoNCkxldCdzIGdlbmVyYXRlIG9uZSBsYXN0IHZpc3VhbGl6YXRpb24gYW5kIGxvb2sgYXQgdGhlIHByb2JhYmlsaXR5IG9mIGhvc3BpdGFsaXphdGlvbiBhZnRlciBjb250cmFjdGluZyBTQVJTLUNvVi0yLiBXZSdsbCBjYWxjdWxhdGUgdGhvc2UgdmFsdWVzIG9uIHRoZSBjdW11bGF0aXZlIGRhdGEgYW5kIHRoZW4gZ3JhcGggdGhlbS4NCg0KKipOb3RlKio6IFdlIGFyZSB3b3JraW5nIHdpdGggY3VtdWxhdGl2ZSB2YWx1ZXMgYWNyb3NzIHRoZSBlbnRpcmUgcGFuZGVtaWMgLSB0aGlzIGRvZXNuJ3QgZ2l2ZSB1cyBhIHNlbnNlIG9mIHRoZSBlZmZlY3Qgb2YgZXh0ZXJuYWwgZWZmZWN0cyBzdWNoIGFzIHZhY2NpbmF0aW9uIHJhdGVzLCBvciBkaWZmZXJlbmNlcyBiZXR3ZWVuIHZhcmlhbnRzIQ0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQojIFN0YXJ0IHdpdGggb3VyIGNvdmlkIGRlbW9ncmFwaGljcyBkYXRhDQpjb3ZpZF9kZW1vZ3JhcGhpY3NfdG90YWwuZGYgJT4lIA0KICAjIFVuZ3JvdXAgdGhlIGRhdGENCiAgdW5ncm91cCgpICU+JSANCiAgDQogICMgRmlsdGVyIGZvciBjdW11bGF0aXZlIGRhdGENCiAgZmlsdGVyKHBlcmlvZCA9PSAiY3VtdWxhdGl2ZSIpICU+JSANCiAgDQogICMgSnVzdCBncmFiIHRoZSBjb2x1bW5zIHdlIHdhbnQgdG8gdXNlDQogIHNlbGVjdChwZXJpb2QsIHB1YmxpY19oZWFsdGhfdW5pdCwgYWdlX2dyb3VwLCB0b3RhbF9jYXNlcywgdG90YWxfaG9zcGl0YWxpemF0aW9uc19jb3VudCkgJT4lIA0KICANCiAgIyBHZW5lcmF0ZSBvdXIgbmV3IGNhbGN1bGF0aW9ucyBhbmQgc2F2ZSB0byBvdXIgZGF0YSBmcmFtZQ0KICBtdXRhdGUocHJvYl9ob3NwaXRhbGl6YXRpb24gPSAuLi4pICU+JSANCiAgDQogICMgRHJvcCBhbnkgcG90ZW50aWFsIE5BIGRhdGEgZHVlIHRvIGRpdmlzaW9uIGJ5IDANCiAgZmlsdGVyKGNvbXBsZXRlLmNhc2VzKC4pKSAlPiUNCiAgDQogIA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeCA9IC4uLiwgeT0uLi4pICsNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgICANCiAgICB4bGFiKCJhZ2UgZ3JvdXAiKSArDQogICAgeWxhYigicHJvYmFiaWxpdHkgb2YgaG9zcGl0YWxpemF0aW9uIikgKw0KICAgIGdndGl0bGUoIlByb2JhYmlsaXR5IG9mIGhvc3BpdGFsaXphdGlvbiBhZnRlciBjb250cmFjdGluZyBTQVJTLUNvVi0yIGZyb20gbXVsdGlwbGUgUEhVcyBhY3Jvc3MgYWdlIGdyb3VwcyIpICsNCg0KICAgICMgNC4gR2VvbXMNCiAgICAjIEFkZCBhIGJveHBsb3QNCiAgICBnZW9tX2JveHBsb3QodmFyd2lkdGg9VFJVRSwgb3V0bGllci5zaGFwZT0uLi4pICsNCg0KICAgICMgQWRkIG91ciBkYXRhIHBvaW50cw0KICAgIGdlb21fcXVhc2lyYW5kb20oYWVzKGNvbG91ciA9IGFnZV9ncm91cCksIHNpemUgPSAzLCBhbHBoYSA9IDAuNykNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyA2LjAuMCBDbGFzcyBzdW1tYXJ5DQoNCldlJ3ZlIGNvdmVyZWQgYSBudW1iZXIgb2Yga2V5IHBsb3RzIHRvZGF5LCBpbmNsdWRpbmcgd2hlbiBhbmQgaG93IHRvIHVzZSB0aGVtLiBOZXh0IHdlZWsgd2UnbGwgcmV2aXNpdCBzb21lIG9mIHRoZXNlIHBsb3RzIGFuZCBzcHJ1Y2UgdGhlbSB1cCB3aXRoIGV4dHJhIHRvdWNoZXMgdGhhdCB3aWxsIHRha2UgdGhlbSB0aGF0IGV4dHJhIGRpc3RhbmNlLiBCZWxvdyB5b3UnbGwgZmluZCBhIHN1bW1hcnkgb2Ygd2hhdCB3ZSd2ZSBkaXNjdXNzZWQgdG9kYXkuDQoNCiMjIDYuMS4wIFN1bW1hcnkgb2YgcGxvdHMNCg0KfCBQbG90ICAgICAgICAgICAgICAgICAgICAgfCBLZXkgRmVhdHVyZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBOb3RlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgU2NhdHRlcnBsb3QgICAgICAgICAgICAgIHwgR29vZCBmb3IgZXhwbG9yaW5nIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMgIHwgQnViYmxlcGxvdHMgYWRkIGFuIGV4dHJhIGRpbWVuc2lvbiB0byB5b3VyIGRhdGEgICAgICAgICAgICB8DQp8IEJhcnBsb3QgICAgICAgICAgICAgICAgICB8IFByZXNlbnQgdmFsdWVzIGFjcm9zcyBncm91cHMuICAgICAgICAgICAgICAgICAgICAgICB8IFByZXNlbnRpbmcgcHJvcG9ydGlvbnMsIHNtYWxsIHNhbXBsZSBzaXplcyAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgfCBTdGFjayBjYXRlZ29yaWVzIGZvciBleHRyYSBkaW1lbnNpb24gICAgICAgICAgICAgICAgfCBEb2VzIG5vdCBkaXNzZWN0IGluZGl2aWR1YWwgZGlzdHJpYnV0aW9ucyAgICAgICAgICAgICAgICAgIHwNCnwgTmlnaHRpbmdhbGUgcGxvdCAgICAgICAgIHwgQ2lyY3VsYXItd2VkZ2UgYmFycGxvdCwgc2FtZSBwcm9wZXJ0aWVzIGFzIGJhcnBsb3QgIHwgUHJlc2VudGluZyBkYXRhIG92ZXIgdW5vcmRlcmVkIGdyb3VwcyAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IFZpc3VhbCBlbXBoYXNpcyBvbiBvdXRlciBhcmVhIHNpemUgbWF5IG1pc2xlYWQgcmVhZGVyICAgICAgfA0KfCBSYWNldHJhY2sgcGxvdCAgICAgICAgICAgfCBDaXJjdWxhci1yaW5nZWQgYmFycGxvdCwgc2FtZSBwcm9wZXJ0aWVzIGFzIGJhcnBsb3QgfCBMb29raW5nIGZvciBhIG1vcmUgY29tcGFjdCB3YXkgdG8gc2hvdyBiYXJwbG90cyAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQ2FsY3VsYXRlIGxlbmd0aCBieSByYWRpYW5zIGFzIG91dGVyIHJpbmdzIGFyZSAic3RyZXRjaGVkIiB8DQp8IERlbnNpdHkgcGxvdCAgICAgICAgICAgICB8IFRoZW9yZXRpY2FsIGRpc3RyaWJ1dGlvbiBvZiB5b3VyIHNhbXBsZSBkYXRhICAgICAgICB8IE1pbmltdW0gc2FtcGxlIHNpemUgNCBidXQgMzAgaXMgbW9yZSByZWxpYWJsZSAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBDYW4gcGxvdCB1cCB0byA1IGRpc3RyaWJ1dGlvbnMgb24gc2FtZSBheGlzICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgVGFpbHMgY2FuIHByb2R1Y2UgImdob3N0IiBkYXRhICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IFJpZGdlcGxvdHMgICAgICAgICAgICAgICB8IEFsbG93cyB0aWdodGVyIHZpc3VhbGl6YXRpb25zIG9mIG11bHRpcGxlIGRlbnNpdGllcyB8IEdvb2Qgd2F5IHRvIHBhY2sgbW9yZSBLREVzIGludG8gYSBzbWFsbGVyIGFyZWEgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgfCBTYW1lIHByb3BlcnRpZXMgYXMgS0RFcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBObyByZWFsIGNvbnRyb2wgb2Ygb3V0bGllcnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgU2ltaWxhciAiZ2hvc3QiIGRhdGEgaXNzdWVzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IEJveCBhbmQgd2hpc2tlciBwbG90ICAgICB8IFN1bW1hcml6ZSBkaXN0cmlidXRpb25zIHdpdGggNSBwYXJhbWV0ZXJzICAgICAgICAgICB8IFBvcHVsYXIgYW5kIGNvbXBhY3QgcHJlc2VudGF0aW9uIG9mIHNpbXBsZSBwb3B1bGF0aW9ucyAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBNaW5pbXVtIHNhbXBsZSBzaXplID0gNSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgRG9lcyBub3QgcHJvcGVybHkgdmlzdWFsaXplIG11bHRpLW1vZGFsIGRhdGEgICAgICAgICAgICAgICB8DQp8IFZpb2xpbiBwbG90cyAgICAgICAgICAgICB8IEJveHBsb3QgZm9ybWF0IHdpdGggS0RFIHZpb2xpbiBzaGFwZSAgICAgICAgICAgICAgICB8IENvbXBhY3QgcmVwcmVzZW50YXRpb24gb2YgZGlzdHJpYnV0aW9uIHNoYXBlICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBMZXNzIHBvcHVsYXIgd2l0aCBudWFuY2VkIGludGVycHJldGF0aW9uICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgSW5oZXJpdHMgImdob3N0IiBkYXRhIGFuZCBvdGhlciBwcm9wZXJ0aWVzIG9mIEtERSAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IERPRVMgaW50ZXJwcmV0IG11bHRpLW1vZGFsIHBvcHVsYXRpb25zICAgICAgICAgICAgICAgICAgICAgfA0KfCBQYXJhbGxlbCBjb29yZGluYXRlIHBsb3QgfCBWaXN1YWwgcmVwcmVzZW50YXRpb24gb2YgbXVsdGl2YXJpYXRlIGRhdGEgICAgICAgICAgfCBDb25uZWN0cyB0cmVuZHMgYWNyb3NzIGdyb3VwcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgUmVsYXRlZCBkYXRhIGNhbiBiZSBjb25uZWN0ZWQgbGluZWFybHkgICAgICAgICAgICAgIHwgTm90IGxpbWl0ZWQgYnkgbnVtYmVyIG9mIHNhbXBsZXMgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IENhbiBoZWxwIGlkZW50aWZ5IHRyZW5kcyB3aXRoaW4gbXVsdGljYXRlZ29yaWNhbCBkYXRhICAgICAgfA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNi4yLjAgV2Vla2x5IGFzc2lnbm1lbnQNCg0KVGhpcyB3ZWVrJ3MgYXNzaWdubWVudCB3aWxsIGJlIGZvdW5kIHVuZGVyIHRoZSBjdXJyZW50IGxlY3R1cmUgZm9sZGVyIHVuZGVyIHRoZSAiYXNzaWdubWVudCIgc3ViZm9sZGVyLiBJdCB3aWxsIGluY2x1ZGUgYW4gUiBtYXJrZG93biBub3RlYm9vayB0aGF0IHlvdSB3aWxsIHVzZSB0byBwcm9kdWNlIHRoZSBjb2RlIGFuZCBhbnN3ZXJzIGZvciB0aGlzIHdlZWsncyBhc3NpZ25tZW50LiBQbGVhc2UgcHJvdmlkZSBhbnN3ZXJzIGluIG1hcmtkb3duIG9yIGNvZGUgY2VsbHMgdGhhdCBpbW1lZGlhdGVseSBmb2xsb3cgZWFjaCBxdWVzdGlvbiBzZWN0aW9uLg0KDQp8ICAgICAgICAgICAgICAgICAgICB8IEFzc2lnbm1lbnQgYnJlYWtkb3duIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCAgICAgICAgQ29kZSAgICAgICAgfCAgICAgICAgIDUwJSAgICAgICAgICB8IFwtIERvZXMgaXQgZm9sbG93IGJlc3QgcHJhY3RpY2VzPyAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBEb2VzIGl0IG1ha2UgZ29vZCB1c2Ugb2YgYXZhaWxhYmxlIHBhY2thZ2VzPyB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gV2FzIGRhdGEgcHJlcGFyZWQgcHJvcGVybHkgICAgICAgICAgICAgICAgICAgfA0KfCBBbnN3ZXJzIGFuZCBPdXRwdXQgfCAgICAgICAgIDUwJSAgICAgICAgICB8IFwtIElzIG91dHB1dCBiYXNlZCBvbiB0aGUgY29ycmVjdCBkYXRhc2V0PyAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBBcmUgZ3JvdXBpbmdzIGFwcHJvcHJpYXRlICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gQXJlIGNvcnJlY3QgdGl0bGVzL2F4ZXMvbGVnZW5kcyBjb3JyZWN0PyAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIElzIGludGVycHJldGF0aW9uIG9mIHRoZSBncmFwaHMgY29ycmVjdD8gICAgIHwNCg0KU2luY2UgY29kaW5nIHN0eWxlcyBhbmQgc29sdXRpb25zIGNhbiBkaWZmZXIsIHN0dWRlbnRzIGFyZSBlbmNvdXJhZ2VkIHRvIHVzZSBiZXN0IHByYWN0aWNlcy4gQXNzaWdubWVudHMgKm1heSogYmUgcmV3YXJkZWQgZm9yIHdlbGwtY29kZWQgb3IgZWxlZ2FudCBzb2x1dGlvbnMuDQoNCllvdSBjYW4gc2F2ZSBhbmQgZG93bmxvYWQgdGhlIG1hcmtkb3duIG5vdGVib29rIGluIGl0cyBuYXRpdmUgZm9ybWF0LiBTdWJtaXQgdGhpcyBmaWxlIHRvIHRoZSB0aGUgYXBwcm9wcmlhdGUgYXNzaWdubWVudCBzZWN0aW9uIGJ5IDEyOjU5IHBtIG9uIHRoZSBkYXRlIG9mIG91ciBuZXh0IGNsYXNzOiBNYXJjaCAyOHRoLCAyMDI0Lg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNi4zLjAgQWNrbm93bGVkZ2VtZW50cw0KDQoqKlJldmlzaW9uIDEuMC4wKio6IGNyZWF0ZWQgYW5kIHByZXBhcmVkIGZvciAqKkNTQjEwMjFIIFMgTEVDMDE0MSoqLCAwMy0yMDIxIGJ5IENhbHZpbiBNb2ssIFBoLkQuICpCaW9pbmZvcm1hdGljaWFuLCBFZHVjYXRpb24gYW5kIE91dHJlYWNoLCBDQUdFRi4qDQoNCioqUmV2aXNpb24gMS4wLjEqKjogZWRpdGVkIGFuZCBwcmVwYXJlZCBmb3IgKipDU0IxMDIwSCBTIExFQzAxNDEqKiwgMDMtMjAyMiBieSBDYWx2aW4gTW9rLCBQaC5ELiAqQmlvaW5mb3JtYXRpY2lhbiwgRWR1Y2F0aW9uIGFuZCBPdXRyZWFjaCwgQ0FHRUYuKg0KDQoqKlJldmlzaW9uIDEuMC4yKio6IGVkaXRlZCBhbmQgcHJlcGFyZWQgZm9yICoqQ1NCMTAyMEggUyBMRUMwMTQxKiosIDAzLTIwMjMgYnkgQ2FsdmluIE1vaywgUGguRC4gKkJpb2luZm9ybWF0aWNpYW4sIEVkdWNhdGlvbiBhbmQgT3V0cmVhY2gsIENBR0VGLioNCg0KKipSZXZpc2lvbiAyLjAuMCoqOiBSZXZpc2VkIGFuZCBwcmVwYXJlZCBmb3IgKipDU0IxMDIwSCBTIExFQzAxNDEqKiwgMDMtMjAyNCBieSBDYWx2aW4gTW9rLCBQaC5ELiAqQmlvaW5mb3JtYXRpY2lhbiwgRWR1Y2F0aW9uIGFuZCBPdXRyZWFjaCwgQ0FHRUYuKg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNi40LjAgUmVmZXJlbmNlcw0KDQoxLiAgUHVibGljIEhlYWx0aCBPbnRhcmlvIGRhdGE6IDxodHRwczovL3d3dy5wdWJsaWNoZWFsdGhvbnRhcmlvLmNhL2VuL2RhdGEtYW5kLWFuYWx5c2lzL3VzaW5nLWRhdGEvb3Blbi1kYXRhPg0KDQoyLiAgQW5zY29tYmUncyBRdWFydGV0OiA8aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQW5zY29tYmUlMjdzX3F1YXJ0ZXQ+DQoNCjMuICBDaG9vc2luZyB0aGUgcmlnaHQgY2hhcnQgdHlwZTogPGh0dHBzOi8vZXh0cmVtZXByZXNlbnRhdGlvbi50eXBlcGFkLmNvbS9ibG9nLzIwMDYvMDkvY2hvb3NpbmdfYV9nb29kLmh0bWw+DQoNCjQuICBBIGxheWVyZWQgZ3JhbW1hciBvZiBncmFwaGljcyBieSBIYWRsZXkgV2lja2hhbTogPGh0dHA6Ly92aXRhLmhhZC5jby5uei9wYXBlcnMvbGF5ZXJlZC1ncmFtbWFyLmh0bWw+DQoNCjUuICBgcmVhZF9kZWxpbSgpYCBjb2x1bW4gdHlwZXMgYW5kIGluZm9ybWF0aW9uOiA8aHR0cHM6Ly9yZWFkci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9yZWFkX2RlbGltLmh0bWw+DQoNCjYuICBQaWUgY2hhcnQgcnVsZXM6IDxodHRwczovL3d3dy4xa2Euc2kvZC9lbi9oZWxwL2ZhcS93aGF0LWlzLXRoZS1tYXhpbXVtLW51bWJlci1vZi1jYXRlZ29yaWVzLXRvLWRpc3BsYXktdXNpbmctcGllLWNoYXJ0cz4NCg0KNy4gIEtlcm5lbCBkZW5zaXR5IGVzdGltYXRpb246IDxodHRwczovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy83Njk0OC93aGF0LWlzLXRoZS1taW5pbXVtLW51bWJlci1vZi1kYXRhLXBvaW50cy1yZXF1aXJlZC1mb3Ita2VybmVsLWRlbnNpdHktZXN0aW1hdGlvbj4NCg0KOC4gIFJpZGdlbGluZSBwbG90czogPGh0dHBzOi8vd3d3LmRhdGEtdG8tdml6LmNvbS9ncmFwaC9yaWRnZWxpbmUuaHRtbD4NCg0KOS4gIFZpc3VhbGl6aW5nIHNhbXBsZXMgd2l0aCBib3hwbG90czogPGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvbm1ldGguMjgxMy5wZGY/b3JpZ2luPXBwdWIjPjpcfjp0ZXh0PVdoZXJlYXMlMjBoaXN0b2dyYW1zJTIwcmVxdWlyZSUyMGElMjBzYW1wbGUscmVuZGVyJTIwaXQlMjBldmVuJTIwbW9yZSUyMGluZm9ybWF0aXZlLg0KDQoxMC4gUmVhc29ucyB0byB1c2UgYSB2aW9saW4gcGxvdDogPGh0dHBzOi8vYmxvZy5iaW90dXJpbmcuY29tLzIwMTgvMDUvMTYvNS1yZWFzb25zLXlvdS1zaG91bGQtdXNlLWEtdmlvbGluLWdyYXBoLz4NCg0KMTEuIFBhcmFsbGVsIGNvb3JkaW5hdGUgcGxvdCBwYXJhbWV0ZXJzOiA8aHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL0dHYWxseS92ZXJzaW9ucy8xLjUuMC90b3BpY3MvZ2dwYXJjb29yZD4NCg0KMTIuIExvdHMgb2YgbW9yZSBwbG90cyBpbiBSITogPGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vaW5kZXguaHRtbD4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIFRoZSBDZW50ZXIgZm9yIHRoZSBBbmFseXNpcyBvZiBHZW5vbWUgRXZvbHV0aW9uIGFuZCBGdW5jdGlvbiAoQ0FHRUYpDQoNClRoZSBDZW50cmUgZm9yIHRoZSBBbmFseXNpcyBvZiBHZW5vbWUgRXZvbHV0aW9uIGFuZCBGdW5jdGlvbiAoQ0FHRUYpIGF0IHRoZSBVbml2ZXJzaXR5IG9mIFRvcm9udG8gb2ZmZXJzIGNvbXByZWhlbnNpdmUgZXhwZXJpbWVudGFsIGRlc2lnbiwgcmVzZWFyY2gsIGFuZCBhbmFseXNpcyBzZXJ2aWNlcyBpbiBtaWNyb2Jpb21lIGFuZCBtZXRhZ2Vub21pYyBzdHVkaWVzLCBnZW5vbWljcywgcHJvdGVvbWljcywgYW5kIGJpb2luZm9ybWF0aWNzLg0KDQpGcm9tIHRhcmdldGVkIEROQSBhbXBsaWNvbiBzZXF1ZW5jaW5nIHRvIHRyYW5zY3JpcHRvbWVzLCB3aG9sZSBnZW5vbWVzLCBhbmQgbWV0YWdlbm9tZXMsIGZyb20gcHJvdGVpbiBpZGVudGlmaWNhdGlvbiB0byBwb3N0LXRyYW5zbGF0aW9uYWwgbW9kaWZpY2F0aW9uLCBDQUdFRiBoYXMgdGhlIHRvb2xzIGFuZCBrbm93bGVkZ2UgdG8gc3VwcG9ydCB5b3VyIHJlc2VhcmNoLiBPdXIgc3RhdGUtb2YtdGhlLWFydCBmYWNpbGl0eSBhbmQgZXhwZXJpZW5jZWQgcmVzZWFyY2ggc3RhZmYgcHJvdmlkZSBhIGJyb2FkIHJhbmdlIG9mIHNlcnZpY2VzLCBpbmNsdWRpbmcgYm90aCBzdGFuZGFyZCBhbmFseXNlcyBhbmQgdGVjaG5pcXVlcyBkZXZlbG9wZWQgYnkgb3VyIHRlYW0uIEluIHBhcnRpY3VsYXIsIHdlIGhhdmUgc3BlY2lhbCBleHBlcnRpc2UgaW4gbWljcm9iaWFsLCBwbGFudCwgYW5kIGVudmlyb25tZW50YWwgc3lzdGVtcy4NCg0KRm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdXMgYW5kIHRoZSBzZXJ2aWNlcyB3ZSBvZmZlciwgcGxlYXNlIHZpc2l0IDxodHRwczovL3d3dy5jYWdlZi51dG9yb250by5jYS8+Lg0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0NBR0VGX25ldy5wbmc/cmF3PXRydWUiIHdpZHRoPSI3MDAiLz4NCjo6Og0K